minesweeper

A minewseeper implementation to play around with Hare and Raylib
git clone https://git.tronto.net/minesweeper
Download | Log | Files | Refs | README | LICENSE

rcore.c (159352B)


      1 /**********************************************************************************************
      2 *
      3 *   rcore - Window/display management, Graphic device/context management and input management
      4 *
      5 *   PLATFORMS SUPPORTED:
      6 *       > PLATFORM_DESKTOP_GLFW (GLFW backend):
      7 *           - Windows (Win32, Win64)
      8 *           - Linux (X11/Wayland desktop mode)
      9 *           - macOS/OSX (x64, arm64)
     10 *           - FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop)
     11 *       > PLATFORM_DESKTOP_SDL (SDL backend):
     12 *           - Windows (Win32, Win64)
     13 *           - Linux (X11/Wayland desktop mode)
     14 *           - Others (not tested)
     15 *       > PLATFORM_WEB:
     16 *           - HTML5 (WebAssembly)
     17 *       > PLATFORM_DRM:
     18 *           - Raspberry Pi 0-5 (DRM/KMS)
     19 *           - Linux DRM subsystem (KMS mode)
     20 *       > PLATFORM_ANDROID:
     21 *           - Android (ARM, ARM64)
     22 *
     23 *   CONFIGURATION:
     24 *       #define SUPPORT_DEFAULT_FONT (default)
     25 *           Default font is loaded on window initialization to be available for the user to render simple text.
     26 *           NOTE: If enabled, uses external module functions to load default raylib font (module: text)
     27 *
     28 *       #define SUPPORT_CAMERA_SYSTEM
     29 *           Camera module is included (rcamera.h) and multiple predefined cameras are available:
     30 *               free, 1st/3rd person, orbital, custom
     31 *
     32 *       #define SUPPORT_GESTURES_SYSTEM
     33 *           Gestures module is included (rgestures.h) to support gestures detection: tap, hold, swipe, drag
     34 *
     35 *       #define SUPPORT_MOUSE_GESTURES
     36 *           Mouse gestures are directly mapped like touches and processed by gestures system.
     37 *
     38 *       #define SUPPORT_BUSY_WAIT_LOOP
     39 *           Use busy wait loop for timing sync, if not defined, a high-resolution timer is setup and used
     40 *
     41 *       #define SUPPORT_PARTIALBUSY_WAIT_LOOP
     42 *           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
     43 *
     44 *       #define SUPPORT_SCREEN_CAPTURE
     45 *           Allow automatic screen capture of current screen pressing F12, defined in KeyCallback()
     46 *
     47 *       #define SUPPORT_GIF_RECORDING
     48 *           Allow automatic gif recording of current screen pressing CTRL+F12, defined in KeyCallback()
     49 *
     50 *       #define SUPPORT_COMPRESSION_API
     51 *           Support CompressData() and DecompressData() functions, those functions use zlib implementation
     52 *           provided by stb_image and stb_image_write libraries, so, those libraries must be enabled on textures module
     53 *           for linkage
     54 *
     55 *       #define SUPPORT_AUTOMATION_EVENTS
     56 *           Support automatic events recording and playing, useful for automated testing systems or AI based game playing
     57 *
     58 *   DEPENDENCIES:
     59 *       raymath  - 3D math functionality (Vector2, Vector3, Matrix, Quaternion)
     60 *       camera   - Multiple 3D camera modes (free, orbital, 1st person, 3rd person)
     61 *       gestures - Gestures system for touch-ready devices (or simulated from mouse inputs)
     62 *
     63 *
     64 *   LICENSE: zlib/libpng
     65 *
     66 *   Copyright (c) 2013-2024 Ramon Santamaria (@raysan5) and contributors
     67 *
     68 *   This software is provided "as-is", without any express or implied warranty. In no event
     69 *   will the authors be held liable for any damages arising from the use of this software.
     70 *
     71 *   Permission is granted to anyone to use this software for any purpose, including commercial
     72 *   applications, and to alter it and redistribute it freely, subject to the following restrictions:
     73 *
     74 *     1. The origin of this software must not be misrepresented; you must not claim that you
     75 *     wrote the original software. If you use this software in a product, an acknowledgment
     76 *     in the product documentation would be appreciated but is not required.
     77 *
     78 *     2. Altered source versions must be plainly marked as such, and must not be misrepresented
     79 *     as being the original software.
     80 *
     81 *     3. This notice may not be removed or altered from any source distribution.
     82 *
     83 **********************************************************************************************/
     84 
     85 //----------------------------------------------------------------------------------
     86 // Feature Test Macros required for this module
     87 //----------------------------------------------------------------------------------
     88 #if (defined(__linux__) || defined(PLATFORM_WEB)) && (_XOPEN_SOURCE < 500)
     89     #undef _XOPEN_SOURCE
     90     #define _XOPEN_SOURCE 500 // Required for: readlink if compiled with c99 without gnu ext.
     91 #endif
     92 
     93 #if (defined(__linux__) || defined(PLATFORM_WEB)) && (_POSIX_C_SOURCE < 199309L)
     94     #undef _POSIX_C_SOURCE
     95     #define _POSIX_C_SOURCE 199309L // Required for: CLOCK_MONOTONIC if compiled with c99 without gnu ext.
     96 #endif
     97 
     98 #include "raylib.h"                 // Declares module functions
     99 
    100 // Check if config flags have been externally provided on compilation line
    101 #if !defined(EXTERNAL_CONFIG_FLAGS)
    102     #include "config.h"             // Defines module configuration flags
    103 #endif
    104 
    105 #include "utils.h"                  // Required for: TRACELOG() macros
    106 
    107 #include <stdlib.h>                 // Required for: srand(), rand(), atexit()
    108 #include <stdio.h>                  // Required for: sprintf() [Used in OpenURL()]
    109 #include <string.h>                 // Required for: strrchr(), strcmp(), strlen(), memset()
    110 #include <time.h>                   // Required for: time() [Used in InitTimer()]
    111 #include <math.h>                   // Required for: tan() [Used in BeginMode3D()], atan2f() [Used in LoadVrStereoConfig()]
    112 
    113 #define RLGL_IMPLEMENTATION
    114 #include "rlgl.h"                   // OpenGL abstraction layer to OpenGL 1.1, 3.3+ or ES2
    115 
    116 #define RAYMATH_IMPLEMENTATION
    117 #include "raymath.h"                // Vector2, Vector3, Quaternion and Matrix functionality
    118 
    119 #if defined(SUPPORT_GESTURES_SYSTEM)
    120     #define RGESTURES_IMPLEMENTATION
    121     #include "rgestures.h"          // Gestures detection functionality
    122 #endif
    123 
    124 #if defined(SUPPORT_CAMERA_SYSTEM)
    125     #define RCAMERA_IMPLEMENTATION
    126     #include "rcamera.h"            // Camera system functionality
    127 #endif
    128 
    129 #if defined(SUPPORT_GIF_RECORDING)
    130     #define MSF_GIF_MALLOC(contextPointer, newSize) RL_MALLOC(newSize)
    131     #define MSF_GIF_REALLOC(contextPointer, oldMemory, oldSize, newSize) RL_REALLOC(oldMemory, newSize)
    132     #define MSF_GIF_FREE(contextPointer, oldMemory, oldSize) RL_FREE(oldMemory)
    133 
    134     #define MSF_GIF_IMPL
    135     #include "external/msf_gif.h"   // GIF recording functionality
    136 #endif
    137 
    138 #if defined(SUPPORT_COMPRESSION_API)
    139     #define SINFL_IMPLEMENTATION
    140     #define SINFL_NO_SIMD
    141     #include "external/sinfl.h"     // Deflate (RFC 1951) decompressor
    142 
    143     #define SDEFL_IMPLEMENTATION
    144     #include "external/sdefl.h"     // Deflate (RFC 1951) compressor
    145 #endif
    146 
    147 #if defined(SUPPORT_RPRAND_GENERATOR)
    148     #define RPRAND_IMPLEMENTATION
    149     #include "external/rprand.h"
    150 #endif
    151 
    152 #if defined(__linux__) && !defined(_GNU_SOURCE)
    153     #define _GNU_SOURCE
    154 #endif
    155 
    156 // Platform specific defines to handle GetApplicationDirectory()
    157 #if (defined(_WIN32) && !defined(PLATFORM_DESKTOP_RGFW)) || (defined(_MSC_VER) && defined(PLATFORM_DESKTOP_RGFW))
    158     #ifndef MAX_PATH
    159         #define MAX_PATH 1025
    160     #endif
    161 __declspec(dllimport) unsigned long __stdcall GetModuleFileNameA(void *hModule, void *lpFilename, unsigned long nSize);
    162 __declspec(dllimport) unsigned long __stdcall GetModuleFileNameW(void *hModule, void *lpFilename, unsigned long nSize);
    163 __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, void *widestr, int cchwide, void *str, int cbmb, void *defchar, int *used_default);
    164 __declspec(dllimport) unsigned int __stdcall timeBeginPeriod(unsigned int uPeriod);
    165 __declspec(dllimport) unsigned int __stdcall timeEndPeriod(unsigned int uPeriod);
    166 #elif defined(__linux__)
    167     #include <unistd.h>
    168 #elif defined(__FreeBSD__)
    169     #include <sys/types.h>
    170     #include <sys/sysctl.h>
    171     #include <unistd.h>
    172 #elif defined(__APPLE__)
    173     #include <sys/syslimits.h>
    174     #include <mach-o/dyld.h>
    175 #endif // OSs
    176 
    177 #define _CRT_INTERNAL_NONSTDC_NAMES  1
    178 #include <sys/stat.h>               // Required for: stat(), S_ISREG [Used in GetFileModTime(), IsFilePath()]
    179 
    180 #if !defined(S_ISREG) && defined(S_IFMT) && defined(S_IFREG)
    181     #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
    182 #endif
    183 
    184 #if defined(_WIN32) && (defined(_MSC_VER) || defined(__TINYC__))
    185     #define DIRENT_MALLOC RL_MALLOC
    186     #define DIRENT_FREE RL_FREE
    187 
    188     #include "external/dirent.h"    // Required for: DIR, opendir(), closedir() [Used in LoadDirectoryFiles()]
    189 #else
    190     #include <dirent.h>             // Required for: DIR, opendir(), closedir() [Used in LoadDirectoryFiles()]
    191 #endif
    192 
    193 #if defined(_WIN32)
    194     #include <io.h>                 // Required for: _access() [Used in FileExists()]
    195     #include <direct.h>             // Required for: _getch(), _chdir(), _mkdir()
    196     #define GETCWD _getcwd          // NOTE: MSDN recommends not to use getcwd(), chdir()
    197     #define CHDIR _chdir
    198     #define MKDIR(dir) _mkdir(dir)
    199 #else
    200     #include <unistd.h>             // Required for: getch(), chdir(), mkdir(), access()
    201     #define GETCWD getcwd
    202     #define CHDIR chdir
    203     #define MKDIR(dir) mkdir(dir, 0777)
    204 #endif
    205 
    206 //----------------------------------------------------------------------------------
    207 // Defines and Macros
    208 //----------------------------------------------------------------------------------
    209 #ifndef MAX_FILEPATH_CAPACITY
    210     #define MAX_FILEPATH_CAPACITY       8192        // Maximum capacity for filepath
    211 #endif
    212 #ifndef MAX_FILEPATH_LENGTH
    213     #if defined(_WIN32)
    214         #define MAX_FILEPATH_LENGTH      256        // On Win32, MAX_PATH = 260 (limits.h) but Windows 10, Version 1607 enables long paths...
    215     #else
    216         #define MAX_FILEPATH_LENGTH     4096        // On Linux, PATH_MAX = 4096 by default (limits.h)
    217     #endif
    218 #endif
    219 
    220 #ifndef MAX_KEYBOARD_KEYS
    221     #define MAX_KEYBOARD_KEYS            512        // Maximum number of keyboard keys supported
    222 #endif
    223 #ifndef MAX_MOUSE_BUTTONS
    224     #define MAX_MOUSE_BUTTONS              8        // Maximum number of mouse buttons supported
    225 #endif
    226 #ifndef MAX_GAMEPADS
    227     #define MAX_GAMEPADS                   4        // Maximum number of gamepads supported
    228 #endif
    229 #ifndef MAX_GAMEPAD_AXIS
    230     #define MAX_GAMEPAD_AXIS               8        // Maximum number of axis supported (per gamepad)
    231 #endif
    232 #ifndef MAX_GAMEPAD_BUTTONS
    233     #define MAX_GAMEPAD_BUTTONS           32        // Maximum number of buttons supported (per gamepad)
    234 #endif
    235 #ifndef MAX_GAMEPAD_VIBRATION_TIME
    236     #define MAX_GAMEPAD_VIBRATION_TIME     2.0f     // Maximum vibration time in seconds
    237 #endif
    238 #ifndef MAX_TOUCH_POINTS
    239     #define MAX_TOUCH_POINTS               8        // Maximum number of touch points supported
    240 #endif
    241 #ifndef MAX_KEY_PRESSED_QUEUE
    242     #define MAX_KEY_PRESSED_QUEUE         16        // Maximum number of keys in the key input queue
    243 #endif
    244 #ifndef MAX_CHAR_PRESSED_QUEUE
    245     #define MAX_CHAR_PRESSED_QUEUE        16        // Maximum number of characters in the char input queue
    246 #endif
    247 
    248 #ifndef MAX_DECOMPRESSION_SIZE
    249     #define MAX_DECOMPRESSION_SIZE        64        // Maximum size allocated for decompression in MB
    250 #endif
    251 
    252 #ifndef MAX_AUTOMATION_EVENTS
    253     #define MAX_AUTOMATION_EVENTS      16384        // Maximum number of automation events to record
    254 #endif
    255 
    256 #ifndef DIRECTORY_FILTER_TAG
    257     #define DIRECTORY_FILTER_TAG       "DIR"        // Name tag used to request directory inclusion on directory scan
    258 #endif                                              // NOTE: Used in ScanDirectoryFiles(), ScanDirectoryFilesRecursively() and LoadDirectoryFilesEx()
    259 
    260 // Flags operation macros
    261 #define FLAG_SET(n, f) ((n) |= (f))
    262 #define FLAG_CLEAR(n, f) ((n) &= ~(f))
    263 #define FLAG_TOGGLE(n, f) ((n) ^= (f))
    264 #define FLAG_CHECK(n, f) ((n) & (f))
    265 
    266 //----------------------------------------------------------------------------------
    267 // Types and Structures Definition
    268 //----------------------------------------------------------------------------------
    269 typedef struct { int x; int y; } Point;
    270 typedef struct { unsigned int width; unsigned int height; } Size;
    271 
    272 // Core global state context data
    273 typedef struct CoreData {
    274     struct {
    275         const char *title;                  // Window text title const pointer
    276         unsigned int flags;                 // Configuration flags (bit based), keeps window state
    277         bool ready;                         // Check if window has been initialized successfully
    278         bool fullscreen;                    // Check if fullscreen mode is enabled
    279         bool shouldClose;                   // Check if window set for closing
    280         bool resizedLastFrame;              // Check if window has been resized last frame
    281         bool eventWaiting;                  // Wait for events before ending frame
    282         bool usingFbo;                      // Using FBO (RenderTexture) for rendering instead of default framebuffer
    283 
    284         Point position;                     // Window position (required on fullscreen toggle)
    285         Point previousPosition;             // Window previous position (required on borderless windowed toggle)
    286         Size display;                       // Display width and height (monitor, device-screen, LCD, ...)
    287         Size screen;                        // Screen width and height (used render area)
    288         Size previousScreen;                // Screen previous width and height (required on borderless windowed toggle)
    289         Size currentFbo;                    // Current render width and height (depends on active fbo)
    290         Size render;                        // Framebuffer width and height (render area, including black bars if required)
    291         Point renderOffset;                 // Offset from render area (must be divided by 2)
    292         Size screenMin;                     // Screen minimum width and height (for resizable window)
    293         Size screenMax;                     // Screen maximum width and height (for resizable window)
    294         Matrix screenScale;                 // Matrix to scale screen (framebuffer rendering)
    295 
    296         char **dropFilepaths;               // Store dropped files paths pointers (provided by GLFW)
    297         unsigned int dropFileCount;         // Count dropped files strings
    298 
    299     } Window;
    300     struct {
    301         const char *basePath;               // Base path for data storage
    302 
    303     } Storage;
    304     struct {
    305         struct {
    306             int exitKey;                    // Default exit key
    307             char currentKeyState[MAX_KEYBOARD_KEYS];        // Registers current frame key state
    308             char previousKeyState[MAX_KEYBOARD_KEYS];       // Registers previous frame key state
    309 
    310             // NOTE: Since key press logic involves comparing prev vs cur key state, we need to handle key repeats specially
    311             char keyRepeatInFrame[MAX_KEYBOARD_KEYS];       // Registers key repeats for current frame
    312 
    313             int keyPressedQueue[MAX_KEY_PRESSED_QUEUE];     // Input keys queue
    314             int keyPressedQueueCount;       // Input keys queue count
    315 
    316             int charPressedQueue[MAX_CHAR_PRESSED_QUEUE];   // Input characters queue (unicode)
    317             int charPressedQueueCount;      // Input characters queue count
    318 
    319         } Keyboard;
    320         struct {
    321             Vector2 offset;                 // Mouse offset
    322             Vector2 scale;                  // Mouse scaling
    323             Vector2 currentPosition;        // Mouse position on screen
    324             Vector2 previousPosition;       // Previous mouse position
    325 
    326             int cursor;                     // Tracks current mouse cursor
    327             bool cursorHidden;              // Track if cursor is hidden
    328             bool cursorOnScreen;            // Tracks if cursor is inside client area
    329 
    330             char currentButtonState[MAX_MOUSE_BUTTONS];     // Registers current mouse button state
    331             char previousButtonState[MAX_MOUSE_BUTTONS];    // Registers previous mouse button state
    332             Vector2 currentWheelMove;       // Registers current mouse wheel variation
    333             Vector2 previousWheelMove;      // Registers previous mouse wheel variation
    334 
    335         } Mouse;
    336         struct {
    337             int pointCount;                             // Number of touch points active
    338             int pointId[MAX_TOUCH_POINTS];              // Point identifiers
    339             Vector2 position[MAX_TOUCH_POINTS];         // Touch position on screen
    340             char currentTouchState[MAX_TOUCH_POINTS];   // Registers current touch state
    341             char previousTouchState[MAX_TOUCH_POINTS];  // Registers previous touch state
    342 
    343         } Touch;
    344         struct {
    345             int lastButtonPressed;          // Register last gamepad button pressed
    346             int axisCount[MAX_GAMEPADS];    // Register number of available gamepad axis
    347             bool ready[MAX_GAMEPADS];       // Flag to know if gamepad is ready
    348             char name[MAX_GAMEPADS][64];    // Gamepad name holder
    349             char currentButtonState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS];     // Current gamepad buttons state
    350             char previousButtonState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS];    // Previous gamepad buttons state
    351             float axisState[MAX_GAMEPADS][MAX_GAMEPAD_AXIS];                // Gamepad axis state
    352 
    353         } Gamepad;
    354     } Input;
    355     struct {
    356         double current;                     // Current time measure
    357         double previous;                    // Previous time measure
    358         double update;                      // Time measure for frame update
    359         double draw;                        // Time measure for frame draw
    360         double frame;                       // Time measure for one frame
    361         double target;                      // Desired time for one frame, if 0 not applied
    362         unsigned long long int base;        // Base time measure for hi-res timer (PLATFORM_ANDROID, PLATFORM_DRM)
    363         unsigned int frameCounter;          // Frame counter
    364 
    365     } Time;
    366 } CoreData;
    367 
    368 //----------------------------------------------------------------------------------
    369 // Global Variables Definition
    370 //----------------------------------------------------------------------------------
    371 RLAPI const char *raylib_version = RAYLIB_VERSION;  // raylib version exported symbol, required for some bindings
    372 
    373 CoreData CORE = { 0 };                      // Global CORE state context
    374 
    375 // Flag to note GPU acceleration is available,
    376 // referenced from other modules to support GPU data loading
    377 // NOTE: Useful to allow Texture, RenderTexture, Font.texture, Mesh.vaoId/vboId, Shader loading
    378 bool isGpuReady = false;
    379 
    380 #if defined(SUPPORT_SCREEN_CAPTURE)
    381 static int screenshotCounter = 0;           // Screenshots counter
    382 #endif
    383 
    384 #if defined(SUPPORT_GIF_RECORDING)
    385 static unsigned int gifFrameCounter = 0;    // GIF frames counter
    386 static bool gifRecording = false;           // GIF recording state
    387 static MsfGifState gifState = { 0 };        // MSGIF context state
    388 #endif
    389 
    390 #if defined(SUPPORT_AUTOMATION_EVENTS)
    391 // Automation events type
    392 typedef enum AutomationEventType {
    393     EVENT_NONE = 0,
    394     // Input events
    395     INPUT_KEY_UP,                   // param[0]: key
    396     INPUT_KEY_DOWN,                 // param[0]: key
    397     INPUT_KEY_PRESSED,              // param[0]: key
    398     INPUT_KEY_RELEASED,             // param[0]: key
    399     INPUT_MOUSE_BUTTON_UP,          // param[0]: button
    400     INPUT_MOUSE_BUTTON_DOWN,        // param[0]: button
    401     INPUT_MOUSE_POSITION,           // param[0]: x, param[1]: y
    402     INPUT_MOUSE_WHEEL_MOTION,       // param[0]: x delta, param[1]: y delta
    403     INPUT_GAMEPAD_CONNECT,          // param[0]: gamepad
    404     INPUT_GAMEPAD_DISCONNECT,       // param[0]: gamepad
    405     INPUT_GAMEPAD_BUTTON_UP,        // param[0]: button
    406     INPUT_GAMEPAD_BUTTON_DOWN,      // param[0]: button
    407     INPUT_GAMEPAD_AXIS_MOTION,      // param[0]: axis, param[1]: delta
    408     INPUT_TOUCH_UP,                 // param[0]: id
    409     INPUT_TOUCH_DOWN,               // param[0]: id
    410     INPUT_TOUCH_POSITION,           // param[0]: x, param[1]: y
    411     INPUT_GESTURE,                  // param[0]: gesture
    412     // Window events
    413     WINDOW_CLOSE,                   // no params
    414     WINDOW_MAXIMIZE,                // no params
    415     WINDOW_MINIMIZE,                // no params
    416     WINDOW_RESIZE,                  // param[0]: width, param[1]: height
    417     // Custom events
    418     ACTION_TAKE_SCREENSHOT,         // no params
    419     ACTION_SETTARGETFPS             // param[0]: fps
    420 } AutomationEventType;
    421 
    422 // Event type to config events flags
    423 // TODO: Not used at the moment
    424 typedef enum {
    425     EVENT_INPUT_KEYBOARD    = 0,
    426     EVENT_INPUT_MOUSE       = 1,
    427     EVENT_INPUT_GAMEPAD     = 2,
    428     EVENT_INPUT_TOUCH       = 4,
    429     EVENT_INPUT_GESTURE     = 8,
    430     EVENT_WINDOW            = 16,
    431     EVENT_CUSTOM            = 32
    432 } EventType;
    433 
    434 // Event type name strings, required for export
    435 static const char *autoEventTypeName[] = {
    436     "EVENT_NONE",
    437     "INPUT_KEY_UP",
    438     "INPUT_KEY_DOWN",
    439     "INPUT_KEY_PRESSED",
    440     "INPUT_KEY_RELEASED",
    441     "INPUT_MOUSE_BUTTON_UP",
    442     "INPUT_MOUSE_BUTTON_DOWN",
    443     "INPUT_MOUSE_POSITION",
    444     "INPUT_MOUSE_WHEEL_MOTION",
    445     "INPUT_GAMEPAD_CONNECT",
    446     "INPUT_GAMEPAD_DISCONNECT",
    447     "INPUT_GAMEPAD_BUTTON_UP",
    448     "INPUT_GAMEPAD_BUTTON_DOWN",
    449     "INPUT_GAMEPAD_AXIS_MOTION",
    450     "INPUT_TOUCH_UP",
    451     "INPUT_TOUCH_DOWN",
    452     "INPUT_TOUCH_POSITION",
    453     "INPUT_GESTURE",
    454     "WINDOW_CLOSE",
    455     "WINDOW_MAXIMIZE",
    456     "WINDOW_MINIMIZE",
    457     "WINDOW_RESIZE",
    458     "ACTION_TAKE_SCREENSHOT",
    459     "ACTION_SETTARGETFPS"
    460 };
    461 
    462 /*
    463 // Automation event (24 bytes)
    464 // NOTE: Opaque struct, internal to raylib
    465 struct AutomationEvent {
    466     unsigned int frame;                 // Event frame
    467     unsigned int type;                  // Event type (AutomationEventType)
    468     int params[4];                      // Event parameters (if required)
    469 };
    470 */
    471 
    472 static AutomationEventList *currentEventList = NULL;        // Current automation events list, set by user, keep internal pointer
    473 static bool automationEventRecording = false;               // Recording automation events flag
    474 //static short automationEventEnabled = 0b0000001111111111; // TODO: Automation events enabled for recording/playing
    475 #endif
    476 //-----------------------------------------------------------------------------------
    477 
    478 //----------------------------------------------------------------------------------
    479 // Module Functions Declaration
    480 // NOTE: Those functions are common for all platforms!
    481 //----------------------------------------------------------------------------------
    482 
    483 #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT)
    484 extern void LoadFontDefault(void);      // [Module: text] Loads default font on InitWindow()
    485 extern void UnloadFontDefault(void);    // [Module: text] Unloads default font from GPU memory
    486 #endif
    487 
    488 extern int InitPlatform(void);          // Initialize platform (graphics, inputs and more)
    489 extern void ClosePlatform(void);        // Close platform
    490 
    491 static void InitTimer(void);                                // Initialize timer, hi-resolution if available (required by InitPlatform())
    492 static void SetupFramebuffer(int width, int height);        // Setup main framebuffer (required by InitPlatform())
    493 static void SetupViewport(int width, int height);           // Set viewport for a provided width and height
    494 
    495 static void ScanDirectoryFiles(const char *basePath, FilePathList *list, const char *filter);   // Scan all files and directories in a base path
    496 static void ScanDirectoryFilesRecursively(const char *basePath, FilePathList *list, const char *filter);  // Scan all files and directories recursively from a base path
    497 
    498 #if defined(SUPPORT_AUTOMATION_EVENTS)
    499 static void RecordAutomationEvent(void); // Record frame events (to internal events array)
    500 #endif
    501 
    502 #if defined(_WIN32) && !defined(PLATFORM_DESKTOP_RGFW)
    503 // NOTE: We declare Sleep() function symbol to avoid including windows.h (kernel32.lib linkage required)
    504 void __stdcall Sleep(unsigned long msTimeout);              // Required for: WaitTime()
    505 #endif
    506 
    507 #if !defined(SUPPORT_MODULE_RTEXT)
    508 const char *TextFormat(const char *text, ...);              // Formatting of text with variables to 'embed'
    509 #endif // !SUPPORT_MODULE_RTEXT
    510 
    511 #if defined(PLATFORM_DESKTOP)
    512     #define PLATFORM_DESKTOP_GLFW
    513 #endif
    514 
    515 // We're using `#pragma message` because `#warning` is not adopted by MSVC.
    516 #if defined(SUPPORT_CLIPBOARD_IMAGE)
    517     #if !defined(SUPPORT_MODULE_RTEXTURES)
    518         #pragma message ("Warning: Enabling SUPPORT_CLIPBOARD_IMAGE requires SUPPORT_MODULE_RTEXTURES to work properly")
    519     #endif
    520 
    521     // It's nice to have support Bitmap on Linux as well, but not as necessary as Windows
    522     #if !defined(SUPPORT_FILEFORMAT_BMP) && defined(_WIN32)
    523         #pragma message ("Warning: Enabling SUPPORT_CLIPBOARD_IMAGE requires SUPPORT_FILEFORMAT_BMP, specially on Windows")
    524     #endif
    525 
    526     // From what I've tested applications on Wayland saves images on clipboard as PNG.
    527     #if (!defined(SUPPORT_FILEFORMAT_PNG) || !defined(SUPPORT_FILEFORMAT_JPG)) && !defined(_WIN32)
    528         #pragma message ("Warning: Getting image from the clipboard might not work without SUPPORT_FILEFORMAT_PNG or SUPPORT_FILEFORMAT_JPG")
    529     #endif
    530 
    531     // Not needed because `rtexture.c` will automatically defined STBI_REQUIRED when any SUPPORT_FILEFORMAT_* is defined.
    532     // #if !defined(STBI_REQUIRED)
    533     //     #pragma message ("Warning: "STBI_REQUIRED is not defined, that means we can't load images from clipbard"
    534     // #endif
    535 
    536 #endif // SUPPORT_CLIPBOARD_IMAGE
    537 
    538 // Include platform-specific submodules
    539 #if defined(PLATFORM_DESKTOP_GLFW)
    540     #include "platforms/rcore_desktop_glfw.c"
    541 #elif defined(PLATFORM_DESKTOP_SDL)
    542     #include "platforms/rcore_desktop_sdl.c"
    543 #elif defined(PLATFORM_DESKTOP_RGFW)
    544     #include "platforms/rcore_desktop_rgfw.c"
    545 #elif defined(PLATFORM_WEB)
    546     #include "platforms/rcore_web.c"
    547 #elif defined(PLATFORM_DRM)
    548     #include "platforms/rcore_drm.c"
    549 #elif defined(PLATFORM_ANDROID)
    550     #include "platforms/rcore_android.c"
    551 #else
    552     // TODO: Include your custom platform backend!
    553     // i.e software rendering backend or console backend!
    554 #endif
    555 
    556 //----------------------------------------------------------------------------------
    557 // Module Functions Definition: Window and Graphics Device
    558 //----------------------------------------------------------------------------------
    559 
    560 // NOTE: Functions with a platform-specific implementation on rcore_<platform>.c
    561 //bool WindowShouldClose(void)
    562 //void ToggleFullscreen(void)
    563 //void ToggleBorderlessWindowed(void)
    564 //void MaximizeWindow(void)
    565 //void MinimizeWindow(void)
    566 //void RestoreWindow(void)
    567 
    568 //void SetWindowState(unsigned int flags)
    569 //void ClearWindowState(unsigned int flags)
    570 
    571 //void SetWindowIcon(Image image)
    572 //void SetWindowIcons(Image *images, int count)
    573 //void SetWindowTitle(const char *title)
    574 //void SetWindowPosition(int x, int y)
    575 //void SetWindowMonitor(int monitor)
    576 //void SetWindowMinSize(int width, int height)
    577 //void SetWindowMaxSize(int width, int height)
    578 //void SetWindowSize(int width, int height)
    579 //void SetWindowOpacity(float opacity)
    580 //void SetWindowFocused(void)
    581 //void *GetWindowHandle(void)
    582 //Vector2 GetWindowPosition(void)
    583 //Vector2 GetWindowScaleDPI(void)
    584 
    585 //int GetMonitorCount(void)
    586 //int GetCurrentMonitor(void)
    587 //int GetMonitorWidth(int monitor)
    588 //int GetMonitorHeight(int monitor)
    589 //int GetMonitorPhysicalWidth(int monitor)
    590 //int GetMonitorPhysicalHeight(int monitor)
    591 //int GetMonitorRefreshRate(int monitor)
    592 //Vector2 GetMonitorPosition(int monitor)
    593 //const char *GetMonitorName(int monitor)
    594 
    595 //void SetClipboardText(const char *text)
    596 //const char *GetClipboardText(void)
    597 
    598 //void ShowCursor(void)
    599 //void HideCursor(void)
    600 //void EnableCursor(void)
    601 //void DisableCursor(void)
    602 
    603 // Initialize window and OpenGL context
    604 void InitWindow(int width, int height, const char *title)
    605 {
    606     TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION);
    607 
    608 #if defined(PLATFORM_DESKTOP_GLFW)
    609     TRACELOG(LOG_INFO, "Platform backend: DESKTOP (GLFW)");
    610 #elif defined(PLATFORM_DESKTOP_SDL)
    611     TRACELOG(LOG_INFO, "Platform backend: DESKTOP (SDL)");
    612 #elif defined(PLATFORM_DESKTOP_RGFW)
    613     TRACELOG(LOG_INFO, "Platform backend: DESKTOP (RGFW)");
    614 #elif defined(PLATFORM_WEB)
    615     TRACELOG(LOG_INFO, "Platform backend: WEB (HTML5)");
    616 #elif defined(PLATFORM_DRM)
    617     TRACELOG(LOG_INFO, "Platform backend: NATIVE DRM");
    618 #elif defined(PLATFORM_ANDROID)
    619     TRACELOG(LOG_INFO, "Platform backend: ANDROID");
    620 #else
    621     // TODO: Include your custom platform backend!
    622     // i.e software rendering backend or console backend!
    623     TRACELOG(LOG_INFO, "Platform backend: CUSTOM");
    624 #endif
    625 
    626     TRACELOG(LOG_INFO, "Supported raylib modules:");
    627     TRACELOG(LOG_INFO, "    > rcore:..... loaded (mandatory)");
    628     TRACELOG(LOG_INFO, "    > rlgl:...... loaded (mandatory)");
    629 #if defined(SUPPORT_MODULE_RSHAPES)
    630     TRACELOG(LOG_INFO, "    > rshapes:... loaded (optional)");
    631 #else
    632     TRACELOG(LOG_INFO, "    > rshapes:... not loaded (optional)");
    633 #endif
    634 #if defined(SUPPORT_MODULE_RTEXTURES)
    635     TRACELOG(LOG_INFO, "    > rtextures:. loaded (optional)");
    636 #else
    637     TRACELOG(LOG_INFO, "    > rtextures:. not loaded (optional)");
    638 #endif
    639 #if defined(SUPPORT_MODULE_RTEXT)
    640     TRACELOG(LOG_INFO, "    > rtext:..... loaded (optional)");
    641 #else
    642     TRACELOG(LOG_INFO, "    > rtext:..... not loaded (optional)");
    643 #endif
    644 #if defined(SUPPORT_MODULE_RMODELS)
    645     TRACELOG(LOG_INFO, "    > rmodels:... loaded (optional)");
    646 #else
    647     TRACELOG(LOG_INFO, "    > rmodels:... not loaded (optional)");
    648 #endif
    649 #if defined(SUPPORT_MODULE_RAUDIO)
    650     TRACELOG(LOG_INFO, "    > raudio:.... loaded (optional)");
    651 #else
    652     TRACELOG(LOG_INFO, "    > raudio:.... not loaded (optional)");
    653 #endif
    654 
    655     // Initialize window data
    656     CORE.Window.screen.width = width;
    657     CORE.Window.screen.height = height;
    658     CORE.Window.eventWaiting = false;
    659     CORE.Window.screenScale = MatrixIdentity();     // No draw scaling required by default
    660     if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title;
    661 
    662     // Initialize global input state
    663     memset(&CORE.Input, 0, sizeof(CORE.Input));     // Reset CORE.Input structure to 0
    664     CORE.Input.Keyboard.exitKey = KEY_ESCAPE;
    665     CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f };
    666     CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW;
    667     CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN;
    668 
    669     // Initialize platform
    670     //--------------------------------------------------------------
    671     InitPlatform();
    672     //--------------------------------------------------------------
    673 
    674     // Initialize rlgl default data (buffers and shaders)
    675     // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl
    676     rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height);
    677     isGpuReady = true; // Flag to note GPU has been initialized successfully
    678 
    679     // Setup default viewport
    680     SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height);
    681 
    682 #if defined(SUPPORT_MODULE_RTEXT)
    683     #if defined(SUPPORT_DEFAULT_FONT)
    684         // Load default font
    685         // WARNING: External function: Module required: rtext
    686         LoadFontDefault();
    687         #if defined(SUPPORT_MODULE_RSHAPES)
    688         // Set font white rectangle for shapes drawing, so shapes and text can be batched together
    689         // WARNING: rshapes module is required, if not available, default internal white rectangle is used
    690         Rectangle rec = GetFontDefault().recs[95];
    691         if (CORE.Window.flags & FLAG_MSAA_4X_HINT)
    692         {
    693             // NOTE: We try to maxime rec padding to avoid pixel bleeding on MSAA filtering
    694             SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 2, rec.y + 2, 1, 1 });
    695         }
    696         else
    697         {
    698             // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding
    699             SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 });
    700         }
    701         #endif
    702     #endif
    703 #else
    704     #if defined(SUPPORT_MODULE_RSHAPES)
    705     // Set default texture and rectangle to be used for shapes drawing
    706     // NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8
    707     Texture2D texture = { rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 };
    708     SetShapesTexture(texture, (Rectangle){ 0.0f, 0.0f, 1.0f, 1.0f });    // WARNING: Module required: rshapes
    709     #endif
    710 #endif
    711 
    712     CORE.Time.frameCounter = 0;
    713     CORE.Window.shouldClose = false;
    714 
    715     // Initialize random seed
    716     SetRandomSeed((unsigned int)time(NULL));
    717 
    718     TRACELOG(LOG_INFO, "SYSTEM: Working Directory: %s", GetWorkingDirectory());
    719 }
    720 
    721 // Close window and unload OpenGL context
    722 void CloseWindow(void)
    723 {
    724 #if defined(SUPPORT_GIF_RECORDING)
    725     if (gifRecording)
    726     {
    727         MsfGifResult result = msf_gif_end(&gifState);
    728         msf_gif_free(result);
    729         gifRecording = false;
    730     }
    731 #endif
    732 
    733 #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT)
    734     UnloadFontDefault();        // WARNING: Module required: rtext
    735 #endif
    736 
    737     rlglClose();                // De-init rlgl
    738 
    739     // De-initialize platform
    740     //--------------------------------------------------------------
    741     ClosePlatform();
    742     //--------------------------------------------------------------
    743 
    744     CORE.Window.ready = false;
    745     TRACELOG(LOG_INFO, "Window closed successfully");
    746 }
    747 
    748 // Check if window has been initialized successfully
    749 bool IsWindowReady(void)
    750 {
    751     return CORE.Window.ready;
    752 }
    753 
    754 // Check if window is currently fullscreen
    755 bool IsWindowFullscreen(void)
    756 {
    757     return CORE.Window.fullscreen;
    758 }
    759 
    760 // Check if window is currently hidden
    761 bool IsWindowHidden(void)
    762 {
    763     return ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0);
    764 }
    765 
    766 // Check if window has been minimized
    767 bool IsWindowMinimized(void)
    768 {
    769     return ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0);
    770 }
    771 
    772 // Check if window has been maximized
    773 bool IsWindowMaximized(void)
    774 {
    775     return ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0);
    776 }
    777 
    778 // Check if window has the focus
    779 bool IsWindowFocused(void)
    780 {
    781     return ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) == 0);
    782 }
    783 
    784 // Check if window has been resizedLastFrame
    785 bool IsWindowResized(void)
    786 {
    787     return CORE.Window.resizedLastFrame;
    788 }
    789 
    790 // Check if one specific window flag is enabled
    791 bool IsWindowState(unsigned int flag)
    792 {
    793     return ((CORE.Window.flags & flag) > 0);
    794 }
    795 
    796 // Get current screen width
    797 int GetScreenWidth(void)
    798 {
    799     return CORE.Window.screen.width;
    800 }
    801 
    802 // Get current screen height
    803 int GetScreenHeight(void)
    804 {
    805     return CORE.Window.screen.height;
    806 }
    807 
    808 // Get current render width which is equal to screen width*dpi scale
    809 int GetRenderWidth(void)
    810 {
    811     int width = 0;
    812 #if defined(__APPLE__)
    813     Vector2 scale = GetWindowScaleDPI();
    814     width = (int)((float)CORE.Window.render.width*scale.x);
    815 #else
    816     width = CORE.Window.render.width;
    817 #endif
    818     return width;
    819 }
    820 
    821 // Get current screen height which is equal to screen height*dpi scale
    822 int GetRenderHeight(void)
    823 {
    824     int height = 0;
    825 #if defined(__APPLE__)
    826     Vector2 scale = GetWindowScaleDPI();
    827     height = (int)((float)CORE.Window.render.height*scale.y);
    828 #else
    829     height = CORE.Window.render.height;
    830 #endif
    831     return height;
    832 }
    833 
    834 // Enable waiting for events on EndDrawing(), no automatic event polling
    835 void EnableEventWaiting(void)
    836 {
    837     CORE.Window.eventWaiting = true;
    838 }
    839 
    840 // Disable waiting for events on EndDrawing(), automatic events polling
    841 void DisableEventWaiting(void)
    842 {
    843     CORE.Window.eventWaiting = false;
    844 }
    845 
    846 // Check if cursor is not visible
    847 bool IsCursorHidden(void)
    848 {
    849     return CORE.Input.Mouse.cursorHidden;
    850 }
    851 
    852 // Check if cursor is on the current screen
    853 bool IsCursorOnScreen(void)
    854 {
    855     return CORE.Input.Mouse.cursorOnScreen;
    856 }
    857 
    858 //----------------------------------------------------------------------------------
    859 // Module Functions Definition: Screen Drawing
    860 //----------------------------------------------------------------------------------
    861 
    862 // Set background color (framebuffer clear color)
    863 void ClearBackground(Color color)
    864 {
    865     rlClearColor(color.r, color.g, color.b, color.a);   // Set clear color
    866     rlClearScreenBuffers();                             // Clear current framebuffers
    867 }
    868 
    869 // Setup canvas (framebuffer) to start drawing
    870 void BeginDrawing(void)
    871 {
    872     // WARNING: Previously to BeginDrawing() other render textures drawing could happen,
    873     // consequently the measure for update vs draw is not accurate (only the total frame time is accurate)
    874 
    875     CORE.Time.current = GetTime();      // Number of elapsed seconds since InitTimer()
    876     CORE.Time.update = CORE.Time.current - CORE.Time.previous;
    877     CORE.Time.previous = CORE.Time.current;
    878 
    879     rlLoadIdentity();                   // Reset current matrix (modelview)
    880     rlMultMatrixf(MatrixToFloat(CORE.Window.screenScale)); // Apply screen scaling
    881 
    882     //rlTranslatef(0.375, 0.375, 0);    // HACK to have 2D pixel-perfect drawing on OpenGL 1.1
    883                                         // NOTE: Not required with OpenGL 3.3+
    884 }
    885 
    886 // End canvas drawing and swap buffers (double buffering)
    887 void EndDrawing(void)
    888 {
    889     rlDrawRenderBatchActive();      // Update and draw internal render batch
    890 
    891 #if defined(SUPPORT_GIF_RECORDING)
    892     // Draw record indicator
    893     if (gifRecording)
    894     {
    895         #ifndef GIF_RECORD_FRAMERATE
    896         #define GIF_RECORD_FRAMERATE    10
    897         #endif
    898         gifFrameCounter += (unsigned int)(GetFrameTime()*1000);
    899 
    900         // NOTE: We record one gif frame depending on the desired gif framerate
    901         if (gifFrameCounter > 1000/GIF_RECORD_FRAMERATE)
    902         {
    903             // Get image data for the current frame (from backbuffer)
    904             // NOTE: This process is quite slow... :(
    905             Vector2 scale = GetWindowScaleDPI();
    906             unsigned char *screenData = rlReadScreenPixels((int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y));
    907 
    908             #ifndef GIF_RECORD_BITRATE
    909             #define GIF_RECORD_BITRATE 16
    910             #endif
    911 
    912             // Add the frame to the gif recording, given how many frames have passed in centiseconds
    913             msf_gif_frame(&gifState, screenData, gifFrameCounter/10, GIF_RECORD_BITRATE, (int)((float)CORE.Window.render.width*scale.x)*4);
    914             gifFrameCounter -= 1000/GIF_RECORD_FRAMERATE;
    915 
    916             RL_FREE(screenData);    // Free image data
    917         }
    918 
    919     #if defined(SUPPORT_MODULE_RSHAPES) && defined(SUPPORT_MODULE_RTEXT)
    920         // Display the recording indicator every half-second
    921         if ((int)(GetTime()/0.5)%2 == 1)
    922         {
    923             DrawCircle(30, CORE.Window.screen.height - 20, 10, MAROON);                 // WARNING: Module required: rshapes
    924             DrawText("GIF RECORDING", 50, CORE.Window.screen.height - 25, 10, RED);     // WARNING: Module required: rtext
    925         }
    926     #endif
    927 
    928         rlDrawRenderBatchActive();  // Update and draw internal render batch
    929     }
    930 #endif
    931 
    932 #if defined(SUPPORT_AUTOMATION_EVENTS)
    933     if (automationEventRecording) RecordAutomationEvent();    // Event recording
    934 #endif
    935 
    936 #if !defined(SUPPORT_CUSTOM_FRAME_CONTROL)
    937     SwapScreenBuffer();                  // Copy back buffer to front buffer (screen)
    938 
    939     // Frame time control system
    940     CORE.Time.current = GetTime();
    941     CORE.Time.draw = CORE.Time.current - CORE.Time.previous;
    942     CORE.Time.previous = CORE.Time.current;
    943 
    944     CORE.Time.frame = CORE.Time.update + CORE.Time.draw;
    945 
    946     // Wait for some milliseconds...
    947     if (CORE.Time.frame < CORE.Time.target)
    948     {
    949         WaitTime(CORE.Time.target - CORE.Time.frame);
    950 
    951         CORE.Time.current = GetTime();
    952         double waitTime = CORE.Time.current - CORE.Time.previous;
    953         CORE.Time.previous = CORE.Time.current;
    954 
    955         CORE.Time.frame += waitTime;    // Total frame time: update + draw + wait
    956     }
    957 
    958     PollInputEvents();      // Poll user events (before next frame update)
    959 #endif
    960 
    961 #if defined(SUPPORT_SCREEN_CAPTURE)
    962     if (IsKeyPressed(KEY_F12))
    963     {
    964 #if defined(SUPPORT_GIF_RECORDING)
    965         if (IsKeyDown(KEY_LEFT_CONTROL))
    966         {
    967             if (gifRecording)
    968             {
    969                 gifRecording = false;
    970 
    971                 MsfGifResult result = msf_gif_end(&gifState);
    972 
    973                 SaveFileData(TextFormat("%s/screenrec%03i.gif", CORE.Storage.basePath, screenshotCounter), result.data, (unsigned int)result.dataSize);
    974                 msf_gif_free(result);
    975 
    976                 TRACELOG(LOG_INFO, "SYSTEM: Finish animated GIF recording");
    977             }
    978             else
    979             {
    980                 gifRecording = true;
    981                 gifFrameCounter = 0;
    982 
    983                 Vector2 scale = GetWindowScaleDPI();
    984                 msf_gif_begin(&gifState, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y));
    985                 screenshotCounter++;
    986 
    987                 TRACELOG(LOG_INFO, "SYSTEM: Start animated GIF recording: %s", TextFormat("screenrec%03i.gif", screenshotCounter));
    988             }
    989         }
    990         else
    991 #endif  // SUPPORT_GIF_RECORDING
    992         {
    993             TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter));
    994             screenshotCounter++;
    995         }
    996     }
    997 #endif  // SUPPORT_SCREEN_CAPTURE
    998 
    999     CORE.Time.frameCounter++;
   1000 }
   1001 
   1002 // Initialize 2D mode with custom camera (2D)
   1003 void BeginMode2D(Camera2D camera)
   1004 {
   1005     rlDrawRenderBatchActive();      // Update and draw internal render batch
   1006 
   1007     rlLoadIdentity();               // Reset current matrix (modelview)
   1008 
   1009     // Apply 2d camera transformation to modelview
   1010     rlMultMatrixf(MatrixToFloat(GetCameraMatrix2D(camera)));
   1011 }
   1012 
   1013 // Ends 2D mode with custom camera
   1014 void EndMode2D(void)
   1015 {
   1016     rlDrawRenderBatchActive();      // Update and draw internal render batch
   1017 
   1018     rlLoadIdentity();               // Reset current matrix (modelview)
   1019 
   1020     if (rlGetActiveFramebuffer() == 0) rlMultMatrixf(MatrixToFloat(CORE.Window.screenScale)); // Apply screen scaling if required
   1021 }
   1022 
   1023 // Initializes 3D mode with custom camera (3D)
   1024 void BeginMode3D(Camera camera)
   1025 {
   1026     rlDrawRenderBatchActive();      // Update and draw internal render batch
   1027 
   1028     rlMatrixMode(RL_PROJECTION);    // Switch to projection matrix
   1029     rlPushMatrix();                 // Save previous matrix, which contains the settings for the 2d ortho projection
   1030     rlLoadIdentity();               // Reset current matrix (projection)
   1031 
   1032     float aspect = (float)CORE.Window.currentFbo.width/(float)CORE.Window.currentFbo.height;
   1033 
   1034     // NOTE: zNear and zFar values are important when computing depth buffer values
   1035     if (camera.projection == CAMERA_PERSPECTIVE)
   1036     {
   1037         // Setup perspective projection
   1038         double top = rlGetCullDistanceNear()*tan(camera.fovy*0.5*DEG2RAD);
   1039         double right = top*aspect;
   1040 
   1041         rlFrustum(-right, right, -top, top, rlGetCullDistanceNear(), rlGetCullDistanceFar());
   1042     }
   1043     else if (camera.projection == CAMERA_ORTHOGRAPHIC)
   1044     {
   1045         // Setup orthographic projection
   1046         double top = camera.fovy/2.0;
   1047         double right = top*aspect;
   1048 
   1049         rlOrtho(-right, right, -top,top, rlGetCullDistanceNear(), rlGetCullDistanceFar());
   1050     }
   1051 
   1052     rlMatrixMode(RL_MODELVIEW);     // Switch back to modelview matrix
   1053     rlLoadIdentity();               // Reset current matrix (modelview)
   1054 
   1055     // Setup Camera view
   1056     Matrix matView = MatrixLookAt(camera.position, camera.target, camera.up);
   1057     rlMultMatrixf(MatrixToFloat(matView));      // Multiply modelview matrix by view matrix (camera)
   1058 
   1059     rlEnableDepthTest();            // Enable DEPTH_TEST for 3D
   1060 }
   1061 
   1062 // Ends 3D mode and returns to default 2D orthographic mode
   1063 void EndMode3D(void)
   1064 {
   1065     rlDrawRenderBatchActive();      // Update and draw internal render batch
   1066 
   1067     rlMatrixMode(RL_PROJECTION);    // Switch to projection matrix
   1068     rlPopMatrix();                  // Restore previous matrix (projection) from matrix stack
   1069 
   1070     rlMatrixMode(RL_MODELVIEW);     // Switch back to modelview matrix
   1071     rlLoadIdentity();               // Reset current matrix (modelview)
   1072 
   1073     if (rlGetActiveFramebuffer() == 0) rlMultMatrixf(MatrixToFloat(CORE.Window.screenScale)); // Apply screen scaling if required
   1074 
   1075     rlDisableDepthTest();           // Disable DEPTH_TEST for 2D
   1076 }
   1077 
   1078 // Initializes render texture for drawing
   1079 void BeginTextureMode(RenderTexture2D target)
   1080 {
   1081     rlDrawRenderBatchActive();      // Update and draw internal render batch
   1082 
   1083     rlEnableFramebuffer(target.id); // Enable render target
   1084 
   1085     // Set viewport and RLGL internal framebuffer size
   1086     rlViewport(0, 0, target.texture.width, target.texture.height);
   1087     rlSetFramebufferWidth(target.texture.width);
   1088     rlSetFramebufferHeight(target.texture.height);
   1089 
   1090     rlMatrixMode(RL_PROJECTION);    // Switch to projection matrix
   1091     rlLoadIdentity();               // Reset current matrix (projection)
   1092 
   1093     // Set orthographic projection to current framebuffer size
   1094     // NOTE: Configured top-left corner as (0, 0)
   1095     rlOrtho(0, target.texture.width, target.texture.height, 0, 0.0f, 1.0f);
   1096 
   1097     rlMatrixMode(RL_MODELVIEW);     // Switch back to modelview matrix
   1098     rlLoadIdentity();               // Reset current matrix (modelview)
   1099 
   1100     //rlScalef(0.0f, -1.0f, 0.0f);  // Flip Y-drawing (?)
   1101 
   1102     // Setup current width/height for proper aspect ratio
   1103     // calculation when using BeginMode3D()
   1104     CORE.Window.currentFbo.width = target.texture.width;
   1105     CORE.Window.currentFbo.height = target.texture.height;
   1106     CORE.Window.usingFbo = true;
   1107 }
   1108 
   1109 // Ends drawing to render texture
   1110 void EndTextureMode(void)
   1111 {
   1112     rlDrawRenderBatchActive();      // Update and draw internal render batch
   1113 
   1114     rlDisableFramebuffer();         // Disable render target (fbo)
   1115 
   1116     // Set viewport to default framebuffer size
   1117     SetupViewport(CORE.Window.render.width, CORE.Window.render.height);
   1118 
   1119     // Go back to the modelview state from BeginDrawing since we are back to the default FBO
   1120     rlMatrixMode(RL_MODELVIEW);     // Switch back to modelview matrix
   1121     rlLoadIdentity();               // Reset current matrix (modelview)
   1122     rlMultMatrixf(MatrixToFloat(CORE.Window.screenScale)); // Apply screen scaling if required
   1123 
   1124     // Reset current fbo to screen size
   1125     CORE.Window.currentFbo.width = CORE.Window.render.width;
   1126     CORE.Window.currentFbo.height = CORE.Window.render.height;
   1127     CORE.Window.usingFbo = false;
   1128 }
   1129 
   1130 // Begin custom shader mode
   1131 void BeginShaderMode(Shader shader)
   1132 {
   1133     rlSetShader(shader.id, shader.locs);
   1134 }
   1135 
   1136 // End custom shader mode (returns to default shader)
   1137 void EndShaderMode(void)
   1138 {
   1139     rlSetShader(rlGetShaderIdDefault(), rlGetShaderLocsDefault());
   1140 }
   1141 
   1142 // Begin blending mode (alpha, additive, multiplied, subtract, custom)
   1143 // NOTE: Blend modes supported are enumerated in BlendMode enum
   1144 void BeginBlendMode(int mode)
   1145 {
   1146     rlSetBlendMode(mode);
   1147 }
   1148 
   1149 // End blending mode (reset to default: alpha blending)
   1150 void EndBlendMode(void)
   1151 {
   1152     rlSetBlendMode(BLEND_ALPHA);
   1153 }
   1154 
   1155 // Begin scissor mode (define screen area for following drawing)
   1156 // NOTE: Scissor rec refers to bottom-left corner, we change it to upper-left
   1157 void BeginScissorMode(int x, int y, int width, int height)
   1158 {
   1159     rlDrawRenderBatchActive();      // Update and draw internal render batch
   1160 
   1161     rlEnableScissorTest();
   1162 
   1163 #if defined(__APPLE__)
   1164     if (!CORE.Window.usingFbo)
   1165     {
   1166         Vector2 scale = GetWindowScaleDPI();
   1167         rlScissor((int)(x*scale.x), (int)(GetScreenHeight()*scale.y - (((y + height)*scale.y))), (int)(width*scale.x), (int)(height*scale.y));
   1168     }
   1169 #else
   1170     if (!CORE.Window.usingFbo && ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0))
   1171     {
   1172         Vector2 scale = GetWindowScaleDPI();
   1173         rlScissor((int)(x*scale.x), (int)(CORE.Window.currentFbo.height - (y + height)*scale.y), (int)(width*scale.x), (int)(height*scale.y));
   1174     }
   1175 #endif
   1176     else
   1177     {
   1178         rlScissor(x, CORE.Window.currentFbo.height - (y + height), width, height);
   1179     }
   1180 }
   1181 
   1182 // End scissor mode
   1183 void EndScissorMode(void)
   1184 {
   1185     rlDrawRenderBatchActive();      // Update and draw internal render batch
   1186     rlDisableScissorTest();
   1187 }
   1188 
   1189 //----------------------------------------------------------------------------------
   1190 // Module Functions Definition: VR Stereo Rendering
   1191 //----------------------------------------------------------------------------------
   1192 
   1193 // Begin VR drawing configuration
   1194 void BeginVrStereoMode(VrStereoConfig config)
   1195 {
   1196     rlEnableStereoRender();
   1197 
   1198     // Set stereo render matrices
   1199     rlSetMatrixProjectionStereo(config.projection[0], config.projection[1]);
   1200     rlSetMatrixViewOffsetStereo(config.viewOffset[0], config.viewOffset[1]);
   1201 }
   1202 
   1203 // End VR drawing process (and desktop mirror)
   1204 void EndVrStereoMode(void)
   1205 {
   1206     rlDisableStereoRender();
   1207 }
   1208 
   1209 // Load VR stereo config for VR simulator device parameters
   1210 VrStereoConfig LoadVrStereoConfig(VrDeviceInfo device)
   1211 {
   1212     VrStereoConfig config = { 0 };
   1213 
   1214     if (rlGetVersion() != RL_OPENGL_11)
   1215     {
   1216         // Compute aspect ratio
   1217         float aspect = ((float)device.hResolution*0.5f)/(float)device.vResolution;
   1218 
   1219         // Compute lens parameters
   1220         float lensShift = (device.hScreenSize*0.25f - device.lensSeparationDistance*0.5f)/device.hScreenSize;
   1221         config.leftLensCenter[0] = 0.25f + lensShift;
   1222         config.leftLensCenter[1] = 0.5f;
   1223         config.rightLensCenter[0] = 0.75f - lensShift;
   1224         config.rightLensCenter[1] = 0.5f;
   1225         config.leftScreenCenter[0] = 0.25f;
   1226         config.leftScreenCenter[1] = 0.5f;
   1227         config.rightScreenCenter[0] = 0.75f;
   1228         config.rightScreenCenter[1] = 0.5f;
   1229 
   1230         // Compute distortion scale parameters
   1231         // NOTE: To get lens max radius, lensShift must be normalized to [-1..1]
   1232         float lensRadius = fabsf(-1.0f - 4.0f*lensShift);
   1233         float lensRadiusSq = lensRadius*lensRadius;
   1234         float distortionScale = device.lensDistortionValues[0] +
   1235                                 device.lensDistortionValues[1]*lensRadiusSq +
   1236                                 device.lensDistortionValues[2]*lensRadiusSq*lensRadiusSq +
   1237                                 device.lensDistortionValues[3]*lensRadiusSq*lensRadiusSq*lensRadiusSq;
   1238 
   1239         float normScreenWidth = 0.5f;
   1240         float normScreenHeight = 1.0f;
   1241         config.scaleIn[0] = 2.0f/normScreenWidth;
   1242         config.scaleIn[1] = 2.0f/normScreenHeight/aspect;
   1243         config.scale[0] = normScreenWidth*0.5f/distortionScale;
   1244         config.scale[1] = normScreenHeight*0.5f*aspect/distortionScale;
   1245 
   1246         // Fovy is normally computed with: 2*atan2f(device.vScreenSize, 2*device.eyeToScreenDistance)
   1247         // ...but with lens distortion it is increased (see Oculus SDK Documentation)
   1248         float fovy = 2.0f*atan2f(device.vScreenSize*0.5f*distortionScale, device.eyeToScreenDistance);     // Really need distortionScale?
   1249        // float fovy = 2.0f*(float)atan2f(device.vScreenSize*0.5f, device.eyeToScreenDistance);
   1250 
   1251         // Compute camera projection matrices
   1252         float projOffset = 4.0f*lensShift;      // Scaled to projection space coordinates [-1..1]
   1253         Matrix proj = MatrixPerspective(fovy, aspect, rlGetCullDistanceNear(), rlGetCullDistanceFar());
   1254 
   1255         config.projection[0] = MatrixMultiply(proj, MatrixTranslate(projOffset, 0.0f, 0.0f));
   1256         config.projection[1] = MatrixMultiply(proj, MatrixTranslate(-projOffset, 0.0f, 0.0f));
   1257 
   1258         // Compute camera transformation matrices
   1259         // NOTE: Camera movement might seem more natural if we model the head
   1260         // Our axis of rotation is the base of our head, so we might want to add
   1261         // some y (base of head to eye level) and -z (center of head to eye protrusion) to the camera positions
   1262         config.viewOffset[0] = MatrixTranslate(device.interpupillaryDistance*0.5f, 0.075f, 0.045f);
   1263         config.viewOffset[1] = MatrixTranslate(-device.interpupillaryDistance*0.5f, 0.075f, 0.045f);
   1264 
   1265         // Compute eyes Viewports
   1266         /*
   1267         config.eyeViewportRight[0] = 0;
   1268         config.eyeViewportRight[1] = 0;
   1269         config.eyeViewportRight[2] = device.hResolution/2;
   1270         config.eyeViewportRight[3] = device.vResolution;
   1271 
   1272         config.eyeViewportLeft[0] = device.hResolution/2;
   1273         config.eyeViewportLeft[1] = 0;
   1274         config.eyeViewportLeft[2] = device.hResolution/2;
   1275         config.eyeViewportLeft[3] = device.vResolution;
   1276         */
   1277     }
   1278     else TRACELOG(LOG_WARNING, "RLGL: VR Simulator not supported on OpenGL 1.1");
   1279 
   1280     return config;
   1281 }
   1282 
   1283 // Unload VR stereo config properties
   1284 void UnloadVrStereoConfig(VrStereoConfig config)
   1285 {
   1286     TRACELOG(LOG_INFO, "UnloadVrStereoConfig not implemented in rcore.c");
   1287 }
   1288 
   1289 //----------------------------------------------------------------------------------
   1290 // Module Functions Definition: Shaders Management
   1291 //----------------------------------------------------------------------------------
   1292 
   1293 // Load shader from files and bind default locations
   1294 // NOTE: If shader string is NULL, using default vertex/fragment shaders
   1295 Shader LoadShader(const char *vsFileName, const char *fsFileName)
   1296 {
   1297     Shader shader = { 0 };
   1298 
   1299     char *vShaderStr = NULL;
   1300     char *fShaderStr = NULL;
   1301 
   1302     if (vsFileName != NULL) vShaderStr = LoadFileText(vsFileName);
   1303     if (fsFileName != NULL) fShaderStr = LoadFileText(fsFileName);
   1304 
   1305     shader = LoadShaderFromMemory(vShaderStr, fShaderStr);
   1306 
   1307     UnloadFileText(vShaderStr);
   1308     UnloadFileText(fShaderStr);
   1309 
   1310     return shader;
   1311 }
   1312 
   1313 // Load shader from code strings and bind default locations
   1314 Shader LoadShaderFromMemory(const char *vsCode, const char *fsCode)
   1315 {
   1316     Shader shader = { 0 };
   1317 
   1318     shader.id = rlLoadShaderCode(vsCode, fsCode);
   1319 
   1320     // After shader loading, we TRY to set default location names
   1321     if (shader.id > 0)
   1322     {
   1323         // Default shader attribute locations have been binded before linking:
   1324         //          vertex position location    = 0
   1325         //          vertex texcoord location    = 1
   1326         //          vertex normal location      = 2
   1327         //          vertex color location       = 3
   1328         //          vertex tangent location     = 4
   1329         //          vertex texcoord2 location   = 5
   1330         //          vertex boneIds location     = 6
   1331         //          vertex boneWeights location = 7
   1332 
   1333         // NOTE: If any location is not found, loc point becomes -1
   1334 
   1335         shader.locs = (int *)RL_CALLOC(RL_MAX_SHADER_LOCATIONS, sizeof(int));
   1336 
   1337         // All locations reset to -1 (no location)
   1338         for (int i = 0; i < RL_MAX_SHADER_LOCATIONS; i++) shader.locs[i] = -1;
   1339 
   1340         // Get handles to GLSL input attribute locations
   1341         shader.locs[SHADER_LOC_VERTEX_POSITION] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION);
   1342         shader.locs[SHADER_LOC_VERTEX_TEXCOORD01] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD);
   1343         shader.locs[SHADER_LOC_VERTEX_TEXCOORD02] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2);
   1344         shader.locs[SHADER_LOC_VERTEX_NORMAL] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL);
   1345         shader.locs[SHADER_LOC_VERTEX_TANGENT] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT);
   1346         shader.locs[SHADER_LOC_VERTEX_COLOR] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR);
   1347         shader.locs[SHADER_LOC_VERTEX_BONEIDS] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_BONEIDS);
   1348         shader.locs[SHADER_LOC_VERTEX_BONEWEIGHTS] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS);
   1349 
   1350         // Get handles to GLSL uniform locations (vertex shader)
   1351         shader.locs[SHADER_LOC_MATRIX_MVP] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_MVP);
   1352         shader.locs[SHADER_LOC_MATRIX_VIEW] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_VIEW);
   1353         shader.locs[SHADER_LOC_MATRIX_PROJECTION] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_PROJECTION);
   1354         shader.locs[SHADER_LOC_MATRIX_MODEL] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_MODEL);
   1355         shader.locs[SHADER_LOC_MATRIX_NORMAL] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_NORMAL);
   1356         shader.locs[SHADER_LOC_BONE_MATRICES] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_BONE_MATRICES);
   1357 
   1358         // Get handles to GLSL uniform locations (fragment shader)
   1359         shader.locs[SHADER_LOC_COLOR_DIFFUSE] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR);
   1360         shader.locs[SHADER_LOC_MAP_DIFFUSE] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0);  // SHADER_LOC_MAP_ALBEDO
   1361         shader.locs[SHADER_LOC_MAP_SPECULAR] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE1); // SHADER_LOC_MAP_METALNESS
   1362         shader.locs[SHADER_LOC_MAP_NORMAL] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE2);
   1363     }
   1364 
   1365     return shader;
   1366 }
   1367 
   1368 // Check if a shader is valid (loaded on GPU)
   1369 bool IsShaderValid(Shader shader)
   1370 {
   1371     return ((shader.id > 0) &&          // Validate shader id (GPU loaded successfully)
   1372             (shader.locs != NULL));     // Validate memory has been allocated for default shader locations
   1373 
   1374     // The following locations are tried to be set automatically (locs[i] >= 0),
   1375     // any of them can be checked for validation but the only mandatory one is, afaik, SHADER_LOC_VERTEX_POSITION
   1376     // NOTE: Users can also setup manually their own attributes/uniforms and do not used the default raylib ones
   1377 
   1378     // Vertex shader attribute locations (default)
   1379     // shader.locs[SHADER_LOC_VERTEX_POSITION]      // Set by default internal shader
   1380     // shader.locs[SHADER_LOC_VERTEX_TEXCOORD01]    // Set by default internal shader
   1381     // shader.locs[SHADER_LOC_VERTEX_TEXCOORD02]
   1382     // shader.locs[SHADER_LOC_VERTEX_NORMAL]
   1383     // shader.locs[SHADER_LOC_VERTEX_TANGENT]
   1384     // shader.locs[SHADER_LOC_VERTEX_COLOR]         // Set by default internal shader
   1385 
   1386     // Vertex shader uniform locations (default)
   1387     // shader.locs[SHADER_LOC_MATRIX_MVP]           // Set by default internal shader
   1388     // shader.locs[SHADER_LOC_MATRIX_VIEW]
   1389     // shader.locs[SHADER_LOC_MATRIX_PROJECTION]
   1390     // shader.locs[SHADER_LOC_MATRIX_MODEL]
   1391     // shader.locs[SHADER_LOC_MATRIX_NORMAL]
   1392 
   1393     // Fragment shader uniform locations (default)
   1394     // shader.locs[SHADER_LOC_COLOR_DIFFUSE]        // Set by default internal shader
   1395     // shader.locs[SHADER_LOC_MAP_DIFFUSE]          // Set by default internal shader
   1396     // shader.locs[SHADER_LOC_MAP_SPECULAR]
   1397     // shader.locs[SHADER_LOC_MAP_NORMAL]
   1398 }
   1399 
   1400 // Unload shader from GPU memory (VRAM)
   1401 void UnloadShader(Shader shader)
   1402 {
   1403     if (shader.id != rlGetShaderIdDefault())
   1404     {
   1405         rlUnloadShaderProgram(shader.id);
   1406 
   1407         // NOTE: If shader loading failed, it should be 0
   1408         RL_FREE(shader.locs);
   1409     }
   1410 }
   1411 
   1412 // Get shader uniform location
   1413 int GetShaderLocation(Shader shader, const char *uniformName)
   1414 {
   1415     return rlGetLocationUniform(shader.id, uniformName);
   1416 }
   1417 
   1418 // Get shader attribute location
   1419 int GetShaderLocationAttrib(Shader shader, const char *attribName)
   1420 {
   1421     return rlGetLocationAttrib(shader.id, attribName);
   1422 }
   1423 
   1424 // Set shader uniform value
   1425 void SetShaderValue(Shader shader, int locIndex, const void *value, int uniformType)
   1426 {
   1427     SetShaderValueV(shader, locIndex, value, uniformType, 1);
   1428 }
   1429 
   1430 // Set shader uniform value vector
   1431 void SetShaderValueV(Shader shader, int locIndex, const void *value, int uniformType, int count)
   1432 {
   1433     if (locIndex > -1)
   1434     {
   1435         rlEnableShader(shader.id);
   1436         rlSetUniform(locIndex, value, uniformType, count);
   1437         //rlDisableShader();      // Avoid resetting current shader program, in case other uniforms are set
   1438     }
   1439 }
   1440 
   1441 // Set shader uniform value (matrix 4x4)
   1442 void SetShaderValueMatrix(Shader shader, int locIndex, Matrix mat)
   1443 {
   1444     if (locIndex > -1)
   1445     {
   1446         rlEnableShader(shader.id);
   1447         rlSetUniformMatrix(locIndex, mat);
   1448         //rlDisableShader();
   1449     }
   1450 }
   1451 
   1452 // Set shader uniform value for texture
   1453 void SetShaderValueTexture(Shader shader, int locIndex, Texture2D texture)
   1454 {
   1455     if (locIndex > -1)
   1456     {
   1457         rlEnableShader(shader.id);
   1458         rlSetUniformSampler(locIndex, texture.id);
   1459         //rlDisableShader();
   1460     }
   1461 }
   1462 
   1463 //----------------------------------------------------------------------------------
   1464 // Module Functions Definition: Screen-space Queries
   1465 //----------------------------------------------------------------------------------
   1466 
   1467 // Get a ray trace from screen position (i.e mouse)
   1468 Ray GetScreenToWorldRay(Vector2 position, Camera camera)
   1469 {
   1470     Ray ray = GetScreenToWorldRayEx(position, camera, GetScreenWidth(), GetScreenHeight());
   1471 
   1472     return ray;
   1473 }
   1474 
   1475 // Get a ray trace from the screen position (i.e mouse) within a specific section of the screen
   1476 Ray GetScreenToWorldRayEx(Vector2 position, Camera camera, int width, int height)
   1477 {
   1478     Ray ray = { 0 };
   1479 
   1480     // Calculate normalized device coordinates
   1481     // NOTE: y value is negative
   1482     float x = (2.0f*position.x)/(float)width - 1.0f;
   1483     float y = 1.0f - (2.0f*position.y)/(float)height;
   1484     float z = 1.0f;
   1485 
   1486     // Store values in a vector
   1487     Vector3 deviceCoords = { x, y, z };
   1488 
   1489     // Calculate view matrix from camera look at
   1490     Matrix matView = MatrixLookAt(camera.position, camera.target, camera.up);
   1491 
   1492     Matrix matProj = MatrixIdentity();
   1493 
   1494     if (camera.projection == CAMERA_PERSPECTIVE)
   1495     {
   1496         // Calculate projection matrix from perspective
   1497         matProj = MatrixPerspective(camera.fovy*DEG2RAD, ((double)width/(double)height), rlGetCullDistanceNear(), rlGetCullDistanceFar());
   1498     }
   1499     else if (camera.projection == CAMERA_ORTHOGRAPHIC)
   1500     {
   1501         double aspect = (double)width/(double)height;
   1502         double top = camera.fovy/2.0;
   1503         double right = top*aspect;
   1504 
   1505         // Calculate projection matrix from orthographic
   1506         matProj = MatrixOrtho(-right, right, -top, top, 0.01, 1000.0);
   1507     }
   1508 
   1509     // Unproject far/near points
   1510     Vector3 nearPoint = Vector3Unproject((Vector3){ deviceCoords.x, deviceCoords.y, 0.0f }, matProj, matView);
   1511     Vector3 farPoint = Vector3Unproject((Vector3){ deviceCoords.x, deviceCoords.y, 1.0f }, matProj, matView);
   1512 
   1513     // Unproject the mouse cursor in the near plane
   1514     // We need this as the source position because orthographic projects,
   1515     // compared to perspective doesn't have a convergence point,
   1516     // meaning that the "eye" of the camera is more like a plane than a point
   1517     Vector3 cameraPlanePointerPos = Vector3Unproject((Vector3){ deviceCoords.x, deviceCoords.y, -1.0f }, matProj, matView);
   1518 
   1519     // Calculate normalized direction vector
   1520     Vector3 direction = Vector3Normalize(Vector3Subtract(farPoint, nearPoint));
   1521 
   1522     if (camera.projection == CAMERA_PERSPECTIVE) ray.position = camera.position;
   1523     else if (camera.projection == CAMERA_ORTHOGRAPHIC) ray.position = cameraPlanePointerPos;
   1524 
   1525     // Apply calculated vectors to ray
   1526     ray.direction = direction;
   1527 
   1528     return ray;
   1529 }
   1530 
   1531 // Get transform matrix for camera
   1532 Matrix GetCameraMatrix(Camera camera)
   1533 {
   1534     Matrix mat = MatrixLookAt(camera.position, camera.target, camera.up);
   1535 
   1536     return mat;
   1537 }
   1538 
   1539 // Get camera 2d transform matrix
   1540 Matrix GetCameraMatrix2D(Camera2D camera)
   1541 {
   1542     Matrix matTransform = { 0 };
   1543     // The camera in world-space is set by
   1544     //   1. Move it to target
   1545     //   2. Rotate by -rotation and scale by (1/zoom)
   1546     //      When setting higher scale, it's more intuitive for the world to become bigger (= camera become smaller),
   1547     //      not for the camera getting bigger, hence the invert. Same deal with rotation
   1548     //   3. Move it by (-offset);
   1549     //      Offset defines target transform relative to screen, but since we're effectively "moving" screen (camera)
   1550     //      we need to do it into opposite direction (inverse transform)
   1551 
   1552     // Having camera transform in world-space, inverse of it gives the modelview transform
   1553     // Since (A*B*C)' = C'*B'*A', the modelview is
   1554     //   1. Move to offset
   1555     //   2. Rotate and Scale
   1556     //   3. Move by -target
   1557     Matrix matOrigin = MatrixTranslate(-camera.target.x, -camera.target.y, 0.0f);
   1558     Matrix matRotation = MatrixRotate((Vector3){ 0.0f, 0.0f, 1.0f }, camera.rotation*DEG2RAD);
   1559     Matrix matScale = MatrixScale(camera.zoom, camera.zoom, 1.0f);
   1560     Matrix matTranslation = MatrixTranslate(camera.offset.x, camera.offset.y, 0.0f);
   1561 
   1562     matTransform = MatrixMultiply(MatrixMultiply(matOrigin, MatrixMultiply(matScale, matRotation)), matTranslation);
   1563 
   1564     return matTransform;
   1565 }
   1566 
   1567 // Get the screen space position from a 3d world space position
   1568 Vector2 GetWorldToScreen(Vector3 position, Camera camera)
   1569 {
   1570     Vector2 screenPosition = GetWorldToScreenEx(position, camera, GetScreenWidth(), GetScreenHeight());
   1571 
   1572     return screenPosition;
   1573 }
   1574 
   1575 // Get size position for a 3d world space position (useful for texture drawing)
   1576 Vector2 GetWorldToScreenEx(Vector3 position, Camera camera, int width, int height)
   1577 {
   1578     // Calculate projection matrix (from perspective instead of frustum
   1579     Matrix matProj = MatrixIdentity();
   1580 
   1581     if (camera.projection == CAMERA_PERSPECTIVE)
   1582     {
   1583         // Calculate projection matrix from perspective
   1584         matProj = MatrixPerspective(camera.fovy*DEG2RAD, ((double)width/(double)height), rlGetCullDistanceNear(), rlGetCullDistanceFar());
   1585     }
   1586     else if (camera.projection == CAMERA_ORTHOGRAPHIC)
   1587     {
   1588         double aspect = (double)width/(double)height;
   1589         double top = camera.fovy/2.0;
   1590         double right = top*aspect;
   1591 
   1592         // Calculate projection matrix from orthographic
   1593         matProj = MatrixOrtho(-right, right, -top, top, rlGetCullDistanceNear(), rlGetCullDistanceFar());
   1594     }
   1595 
   1596     // Calculate view matrix from camera look at (and transpose it)
   1597     Matrix matView = MatrixLookAt(camera.position, camera.target, camera.up);
   1598 
   1599     // TODO: Why not use Vector3Transform(Vector3 v, Matrix mat)?
   1600 
   1601     // Convert world position vector to quaternion
   1602     Quaternion worldPos = { position.x, position.y, position.z, 1.0f };
   1603 
   1604     // Transform world position to view
   1605     worldPos = QuaternionTransform(worldPos, matView);
   1606 
   1607     // Transform result to projection (clip space position)
   1608     worldPos = QuaternionTransform(worldPos, matProj);
   1609 
   1610     // Calculate normalized device coordinates (inverted y)
   1611     Vector3 ndcPos = { worldPos.x/worldPos.w, -worldPos.y/worldPos.w, worldPos.z/worldPos.w };
   1612 
   1613     // Calculate 2d screen position vector
   1614     Vector2 screenPosition = { (ndcPos.x + 1.0f)/2.0f*(float)width, (ndcPos.y + 1.0f)/2.0f*(float)height };
   1615 
   1616     return screenPosition;
   1617 }
   1618 
   1619 // Get the screen space position for a 2d camera world space position
   1620 Vector2 GetWorldToScreen2D(Vector2 position, Camera2D camera)
   1621 {
   1622     Matrix matCamera = GetCameraMatrix2D(camera);
   1623     Vector3 transform = Vector3Transform((Vector3){ position.x, position.y, 0 }, matCamera);
   1624 
   1625     return (Vector2){ transform.x, transform.y };
   1626 }
   1627 
   1628 // Get the world space position for a 2d camera screen space position
   1629 Vector2 GetScreenToWorld2D(Vector2 position, Camera2D camera)
   1630 {
   1631     Matrix invMatCamera = MatrixInvert(GetCameraMatrix2D(camera));
   1632     Vector3 transform = Vector3Transform((Vector3){ position.x, position.y, 0 }, invMatCamera);
   1633 
   1634     return (Vector2){ transform.x, transform.y };
   1635 }
   1636 
   1637 //----------------------------------------------------------------------------------
   1638 // Module Functions Definition: Timming
   1639 //----------------------------------------------------------------------------------
   1640 
   1641 // NOTE: Functions with a platform-specific implementation on rcore_<platform>.c
   1642 //double GetTime(void)
   1643 
   1644 // Set target FPS (maximum)
   1645 void SetTargetFPS(int fps)
   1646 {
   1647     if (fps < 1) CORE.Time.target = 0.0;
   1648     else CORE.Time.target = 1.0/(double)fps;
   1649 
   1650     TRACELOG(LOG_INFO, "TIMER: Target time per frame: %02.03f milliseconds", (float)CORE.Time.target*1000.0f);
   1651 }
   1652 
   1653 // Get current FPS
   1654 // NOTE: We calculate an average framerate
   1655 int GetFPS(void)
   1656 {
   1657     int fps = 0;
   1658 
   1659 #if !defined(SUPPORT_CUSTOM_FRAME_CONTROL)
   1660     #define FPS_CAPTURE_FRAMES_COUNT    30      // 30 captures
   1661     #define FPS_AVERAGE_TIME_SECONDS   0.5f     // 500 milliseconds
   1662     #define FPS_STEP (FPS_AVERAGE_TIME_SECONDS/FPS_CAPTURE_FRAMES_COUNT)
   1663 
   1664     static int index = 0;
   1665     static float history[FPS_CAPTURE_FRAMES_COUNT] = { 0 };
   1666     static float average = 0, last = 0;
   1667     float fpsFrame = GetFrameTime();
   1668 
   1669     // if we reset the window, reset the FPS info
   1670     if (CORE.Time.frameCounter == 0)
   1671     {
   1672         average = 0;
   1673         last = 0;
   1674         index = 0;
   1675 
   1676         for (int i = 0; i < FPS_CAPTURE_FRAMES_COUNT; i++) history[i] = 0;
   1677     }
   1678 
   1679     if (fpsFrame == 0) return 0;
   1680 
   1681     if ((GetTime() - last) > FPS_STEP)
   1682     {
   1683         last = (float)GetTime();
   1684         index = (index + 1)%FPS_CAPTURE_FRAMES_COUNT;
   1685         average -= history[index];
   1686         history[index] = fpsFrame/FPS_CAPTURE_FRAMES_COUNT;
   1687         average += history[index];
   1688     }
   1689 
   1690     fps = (int)roundf(1.0f/average);
   1691 #endif
   1692 
   1693     return fps;
   1694 }
   1695 
   1696 // Get time in seconds for last frame drawn (delta time)
   1697 float GetFrameTime(void)
   1698 {
   1699     return (float)CORE.Time.frame;
   1700 }
   1701 
   1702 //----------------------------------------------------------------------------------
   1703 // Module Functions Definition: Custom frame control
   1704 //----------------------------------------------------------------------------------
   1705 
   1706 // NOTE: Functions with a platform-specific implementation on rcore_<platform>.c
   1707 //void SwapScreenBuffer(void);
   1708 //void PollInputEvents(void);
   1709 
   1710 // Wait for some time (stop program execution)
   1711 // NOTE: Sleep() granularity could be around 10 ms, it means, Sleep() could
   1712 // take longer than expected... for that reason we use the busy wait loop
   1713 // Ref: http://stackoverflow.com/questions/43057578/c-programming-win32-games-sleep-taking-longer-than-expected
   1714 // Ref: http://www.geisswerks.com/ryan/FAQS/timing.html --> All about timing on Win32!
   1715 void WaitTime(double seconds)
   1716 {
   1717     if (seconds < 0) return;    // Security check
   1718 
   1719 #if defined(SUPPORT_BUSY_WAIT_LOOP) || defined(SUPPORT_PARTIALBUSY_WAIT_LOOP)
   1720     double destinationTime = GetTime() + seconds;
   1721 #endif
   1722 
   1723 #if defined(SUPPORT_BUSY_WAIT_LOOP)
   1724     while (GetTime() < destinationTime) { }
   1725 #else
   1726     #if defined(SUPPORT_PARTIALBUSY_WAIT_LOOP)
   1727         double sleepSeconds = seconds - seconds*0.05;  // NOTE: We reserve a percentage of the time for busy waiting
   1728     #else
   1729         double sleepSeconds = seconds;
   1730     #endif
   1731 
   1732     // System halt functions
   1733     #if defined(_WIN32)
   1734         Sleep((unsigned long)(sleepSeconds*1000.0));
   1735     #endif
   1736     #if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__EMSCRIPTEN__)
   1737         struct timespec req = { 0 };
   1738         time_t sec = sleepSeconds;
   1739         long nsec = (sleepSeconds - sec)*1000000000L;
   1740         req.tv_sec = sec;
   1741         req.tv_nsec = nsec;
   1742 
   1743         // NOTE: Use nanosleep() on Unix platforms... usleep() it's deprecated
   1744         while (nanosleep(&req, &req) == -1) continue;
   1745     #endif
   1746     #if defined(__APPLE__)
   1747         usleep(sleepSeconds*1000000.0);
   1748     #endif
   1749 
   1750     #if defined(SUPPORT_PARTIALBUSY_WAIT_LOOP)
   1751         while (GetTime() < destinationTime) { }
   1752     #endif
   1753 #endif
   1754 }
   1755 
   1756 //----------------------------------------------------------------------------------
   1757 // Module Functions Definition: Misc
   1758 //----------------------------------------------------------------------------------
   1759 
   1760 // NOTE: Functions with a platform-specific implementation on rcore_<platform>.c
   1761 //void OpenURL(const char *url)
   1762 
   1763 // Set the seed for the random number generator
   1764 void SetRandomSeed(unsigned int seed)
   1765 {
   1766 #if defined(SUPPORT_RPRAND_GENERATOR)
   1767     rprand_set_seed(seed);
   1768 #else
   1769     srand(seed);
   1770 #endif
   1771 }
   1772 
   1773 // Get a random value between min and max included
   1774 int GetRandomValue(int min, int max)
   1775 {
   1776     int value = 0;
   1777 
   1778     if (min > max)
   1779     {
   1780         int tmp = max;
   1781         max = min;
   1782         min = tmp;
   1783     }
   1784 
   1785 #if defined(SUPPORT_RPRAND_GENERATOR)
   1786     value = rprand_get_value(min, max);
   1787 #else
   1788     // WARNING: Ranges higher than RAND_MAX will return invalid results
   1789     // More specifically, if (max - min) > INT_MAX there will be an overflow,
   1790     // and otherwise if (max - min) > RAND_MAX the random value will incorrectly never exceed a certain threshold
   1791     // NOTE: Depending on the library it can be as low as 32767
   1792     if ((unsigned int)(max - min) > (unsigned int)RAND_MAX)
   1793     {
   1794         TRACELOG(LOG_WARNING, "Invalid GetRandomValue() arguments, range should not be higher than %i", RAND_MAX);
   1795     }
   1796 
   1797     value = (rand()%(abs(max - min) + 1) + min);
   1798 #endif
   1799     return value;
   1800 }
   1801 
   1802 // Load random values sequence, no values repeated, min and max included
   1803 int *LoadRandomSequence(unsigned int count, int min, int max)
   1804 {
   1805     int *values = NULL;
   1806 
   1807 #if defined(SUPPORT_RPRAND_GENERATOR)
   1808     values = rprand_load_sequence(count, min, max);
   1809 #else
   1810     if (count > ((unsigned int)abs(max - min) + 1)) return values;  // Security check
   1811 
   1812     values = (int *)RL_CALLOC(count, sizeof(int));
   1813 
   1814     int value = 0;
   1815     bool dupValue = false;
   1816 
   1817     for (int i = 0; i < (int)count;)
   1818     {
   1819         value = (rand()%(abs(max - min) + 1) + min);
   1820         dupValue = false;
   1821 
   1822         for (int j = 0; j < i; j++)
   1823         {
   1824             if (values[j] == value)
   1825             {
   1826                 dupValue = true;
   1827                 break;
   1828             }
   1829         }
   1830 
   1831         if (!dupValue)
   1832         {
   1833             values[i] = value;
   1834             i++;
   1835         }
   1836     }
   1837 #endif
   1838     return values;
   1839 }
   1840 
   1841 // Unload random values sequence
   1842 void UnloadRandomSequence(int *sequence)
   1843 {
   1844 #if defined(SUPPORT_RPRAND_GENERATOR)
   1845     rprand_unload_sequence(sequence);
   1846 #else
   1847     RL_FREE(sequence);
   1848 #endif
   1849 }
   1850 
   1851 // Takes a screenshot of current screen
   1852 // NOTE: Provided fileName should not contain paths, saving to working directory
   1853 void TakeScreenshot(const char *fileName)
   1854 {
   1855 #if defined(SUPPORT_MODULE_RTEXTURES)
   1856     // Security check to (partially) avoid malicious code
   1857     if (strchr(fileName, '\'') != NULL) { TRACELOG(LOG_WARNING, "SYSTEM: Provided fileName could be potentially malicious, avoid [\'] character"); return; }
   1858 
   1859     Vector2 scale = GetWindowScaleDPI();
   1860     unsigned char *imgData = rlReadScreenPixels((int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y));
   1861     Image image = { imgData, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y), 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 };
   1862 
   1863     char path[512] = { 0 };
   1864     strcpy(path, TextFormat("%s/%s", CORE.Storage.basePath, GetFileName(fileName)));
   1865 
   1866     ExportImage(image, path);           // WARNING: Module required: rtextures
   1867     RL_FREE(imgData);
   1868 
   1869     if (FileExists(path)) TRACELOG(LOG_INFO, "SYSTEM: [%s] Screenshot taken successfully", path);
   1870     else TRACELOG(LOG_WARNING, "SYSTEM: [%s] Screenshot could not be saved", path);
   1871 #else
   1872     TRACELOG(LOG_WARNING,"IMAGE: ExportImage() requires module: rtextures");
   1873 #endif
   1874 }
   1875 
   1876 // Setup window configuration flags (view FLAGS)
   1877 // NOTE: This function is expected to be called before window creation,
   1878 // because it sets up some flags for the window creation process
   1879 // To configure window states after creation, just use SetWindowState()
   1880 void SetConfigFlags(unsigned int flags)
   1881 {
   1882     // Selected flags are set but not evaluated at this point,
   1883     // flag evaluation happens at InitWindow() or SetWindowState()
   1884     CORE.Window.flags |= flags;
   1885 }
   1886 
   1887 //----------------------------------------------------------------------------------
   1888 // Module Functions Definition: File system
   1889 //----------------------------------------------------------------------------------
   1890 
   1891 // Check if the file exists
   1892 bool FileExists(const char *fileName)
   1893 {
   1894     bool result = false;
   1895 
   1896 #if defined(_WIN32)
   1897     if (_access(fileName, 0) != -1) result = true;
   1898 #else
   1899     if (access(fileName, F_OK) != -1) result = true;
   1900 #endif
   1901 
   1902     // NOTE: Alternatively, stat() can be used instead of access()
   1903     //#include <sys/stat.h>
   1904     //struct stat statbuf;
   1905     //if (stat(filename, &statbuf) == 0) result = true;
   1906 
   1907     return result;
   1908 }
   1909 
   1910 // Check file extension
   1911 // NOTE: Extensions checking is not case-sensitive
   1912 bool IsFileExtension(const char *fileName, const char *ext)
   1913 {
   1914     #define MAX_FILE_EXTENSION_LENGTH  16
   1915 
   1916     bool result = false;
   1917     const char *fileExt = GetFileExtension(fileName);
   1918 
   1919     if (fileExt != NULL)
   1920     {
   1921 #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_TEXT_MANIPULATION)
   1922         int extCount = 0;
   1923         const char **checkExts = TextSplit(ext, ';', &extCount); // WARNING: Module required: rtext
   1924 
   1925         char fileExtLower[MAX_FILE_EXTENSION_LENGTH + 1] = { 0 };
   1926         strncpy(fileExtLower, TextToLower(fileExt), MAX_FILE_EXTENSION_LENGTH); // WARNING: Module required: rtext
   1927 
   1928         for (int i = 0; i < extCount; i++)
   1929         {
   1930             if (strcmp(fileExtLower, TextToLower(checkExts[i])) == 0)
   1931             {
   1932                 result = true;
   1933                 break;
   1934             }
   1935         }
   1936 #else
   1937         if (strcmp(fileExt, ext) == 0) result = true;
   1938 #endif
   1939     }
   1940 
   1941     return result;
   1942 }
   1943 
   1944 // Check if a directory path exists
   1945 bool DirectoryExists(const char *dirPath)
   1946 {
   1947     bool result = false;
   1948     DIR *dir = opendir(dirPath);
   1949 
   1950     if (dir != NULL)
   1951     {
   1952         result = true;
   1953         closedir(dir);
   1954     }
   1955 
   1956     return result;
   1957 }
   1958 
   1959 // Get file length in bytes
   1960 // NOTE: GetFileSize() conflicts with windows.h
   1961 int GetFileLength(const char *fileName)
   1962 {
   1963     int size = 0;
   1964 
   1965     // NOTE: On Unix-like systems, it can by used the POSIX system call: stat(),
   1966     // but depending on the platform that call could not be available
   1967     //struct stat result = { 0 };
   1968     //stat(fileName, &result);
   1969     //return result.st_size;
   1970 
   1971     FILE *file = fopen(fileName, "rb");
   1972 
   1973     if (file != NULL)
   1974     {
   1975         fseek(file, 0L, SEEK_END);
   1976         long int fileSize = ftell(file);
   1977 
   1978         // Check for size overflow (INT_MAX)
   1979         if (fileSize > 2147483647) TRACELOG(LOG_WARNING, "[%s] File size overflows expected limit, do not use GetFileLength()", fileName);
   1980         else size = (int)fileSize;
   1981 
   1982         fclose(file);
   1983     }
   1984 
   1985     return size;
   1986 }
   1987 
   1988 // Get pointer to extension for a filename string (includes the dot: .png)
   1989 const char *GetFileExtension(const char *fileName)
   1990 {
   1991     const char *dot = strrchr(fileName, '.');
   1992 
   1993     if (!dot || dot == fileName) return NULL;
   1994 
   1995     return dot;
   1996 }
   1997 
   1998 // String pointer reverse break: returns right-most occurrence of charset in s
   1999 static const char *strprbrk(const char *s, const char *charset)
   2000 {
   2001     const char *latestMatch = NULL;
   2002 
   2003     for (; s = strpbrk(s, charset), s != NULL; latestMatch = s++) { }
   2004 
   2005     return latestMatch;
   2006 }
   2007 
   2008 // Get pointer to filename for a path string
   2009 const char *GetFileName(const char *filePath)
   2010 {
   2011     const char *fileName = NULL;
   2012 
   2013     if (filePath != NULL) fileName = strprbrk(filePath, "\\/");
   2014 
   2015     if (fileName == NULL) return filePath;
   2016 
   2017     return fileName + 1;
   2018 }
   2019 
   2020 // Get filename string without extension (uses static string)
   2021 const char *GetFileNameWithoutExt(const char *filePath)
   2022 {
   2023     #define MAX_FILENAME_LENGTH     256
   2024 
   2025     static char fileName[MAX_FILENAME_LENGTH] = { 0 };
   2026     memset(fileName, 0, MAX_FILENAME_LENGTH);
   2027 
   2028     if (filePath != NULL)
   2029     {
   2030         strcpy(fileName, GetFileName(filePath)); // Get filename.ext without path
   2031         int size = (int)strlen(fileName); // Get size in bytes
   2032 
   2033         for (int i = size; i > 0; i--) // Reverse search '.'
   2034         {
   2035             if (fileName[i] == '.')
   2036             {
   2037                 // NOTE: We break on first '.' found
   2038                 fileName[i] = '\0';
   2039                 break;
   2040             }
   2041         }
   2042     }
   2043 
   2044     return fileName;
   2045 }
   2046 
   2047 // Get directory for a given filePath
   2048 const char *GetDirectoryPath(const char *filePath)
   2049 {
   2050     /*
   2051     // NOTE: Directory separator is different in Windows and other platforms,
   2052     // fortunately, Windows also support the '/' separator, that's the one should be used
   2053     #if defined(_WIN32)
   2054         char separator = '\\';
   2055     #else
   2056         char separator = '/';
   2057     #endif
   2058     */
   2059     const char *lastSlash = NULL;
   2060     static char dirPath[MAX_FILEPATH_LENGTH] = { 0 };
   2061     memset(dirPath, 0, MAX_FILEPATH_LENGTH);
   2062 
   2063     // In case provided path does not contain a root drive letter (C:\, D:\) nor leading path separator (\, /),
   2064     // we add the current directory path to dirPath
   2065     if (filePath[1] != ':' && filePath[0] != '\\' && filePath[0] != '/')
   2066     {
   2067         // For security, we set starting path to current directory,
   2068         // obtained path will be concatenated to this
   2069         dirPath[0] = '.';
   2070         dirPath[1] = '/';
   2071     }
   2072 
   2073     lastSlash = strprbrk(filePath, "\\/");
   2074     if (lastSlash)
   2075     {
   2076         if (lastSlash == filePath)
   2077         {
   2078             // The last and only slash is the leading one: path is in a root directory
   2079             dirPath[0] = filePath[0];
   2080             dirPath[1] = '\0';
   2081         }
   2082         else
   2083         {
   2084             // NOTE: Be careful, strncpy() is not safe, it does not care about '\0'
   2085             char *dirPathPtr = dirPath;
   2086             if ((filePath[1] != ':') && (filePath[0] != '\\') && (filePath[0] != '/')) dirPathPtr += 2;     // Skip drive letter, "C:"
   2087             memcpy(dirPathPtr, filePath, strlen(filePath) - (strlen(lastSlash) - 1));
   2088             dirPath[strlen(filePath) - strlen(lastSlash) + (((filePath[1] != ':') && (filePath[0] != '\\') && (filePath[0] != '/'))? 2 : 0)] = '\0';  // Add '\0' manually
   2089         }
   2090     }
   2091 
   2092     return dirPath;
   2093 }
   2094 
   2095 // Get previous directory path for a given path
   2096 const char *GetPrevDirectoryPath(const char *dirPath)
   2097 {
   2098     static char prevDirPath[MAX_FILEPATH_LENGTH] = { 0 };
   2099     memset(prevDirPath, 0, MAX_FILEPATH_LENGTH);
   2100     int pathLen = (int)strlen(dirPath);
   2101 
   2102     if (pathLen <= 3) strcpy(prevDirPath, dirPath);
   2103 
   2104     for (int i = (pathLen - 1); (i >= 0) && (pathLen > 3); i--)
   2105     {
   2106         if ((dirPath[i] == '\\') || (dirPath[i] == '/'))
   2107         {
   2108             // Check for root: "C:\" or "/"
   2109             if (((i == 2) && (dirPath[1] ==':')) || (i == 0)) i++;
   2110 
   2111             strncpy(prevDirPath, dirPath, i);
   2112             break;
   2113         }
   2114     }
   2115 
   2116     return prevDirPath;
   2117 }
   2118 
   2119 // Get current working directory
   2120 const char *GetWorkingDirectory(void)
   2121 {
   2122     static char currentDir[MAX_FILEPATH_LENGTH] = { 0 };
   2123     memset(currentDir, 0, MAX_FILEPATH_LENGTH);
   2124 
   2125     char *path = GETCWD(currentDir, MAX_FILEPATH_LENGTH - 1);
   2126 
   2127     return path;
   2128 }
   2129 
   2130 const char *GetApplicationDirectory(void)
   2131 {
   2132     static char appDir[MAX_FILEPATH_LENGTH] = { 0 };
   2133     memset(appDir, 0, MAX_FILEPATH_LENGTH);
   2134 
   2135 #if defined(_WIN32)
   2136     int len = 0;
   2137 #if defined(UNICODE)
   2138     unsigned short widePath[MAX_PATH];
   2139     len = GetModuleFileNameW(NULL, widePath, MAX_PATH);
   2140     len = WideCharToMultiByte(0, 0, widePath, len, appDir, MAX_PATH, NULL, NULL);
   2141 #else
   2142     len = GetModuleFileNameA(NULL, appDir, MAX_PATH);
   2143 #endif
   2144     if (len > 0)
   2145     {
   2146         for (int i = len; i >= 0; --i)
   2147         {
   2148             if (appDir[i] == '\\')
   2149             {
   2150                 appDir[i + 1] = '\0';
   2151                 break;
   2152             }
   2153         }
   2154     }
   2155     else
   2156     {
   2157         appDir[0] = '.';
   2158         appDir[1] = '\\';
   2159     }
   2160 
   2161 #elif defined(__linux__)
   2162     unsigned int size = sizeof(appDir);
   2163     ssize_t len = readlink("/proc/self/exe", appDir, size);
   2164 
   2165     if (len > 0)
   2166     {
   2167         for (int i = len; i >= 0; --i)
   2168         {
   2169             if (appDir[i] == '/')
   2170             {
   2171                 appDir[i + 1] = '\0';
   2172                 break;
   2173             }
   2174         }
   2175     }
   2176     else
   2177     {
   2178         appDir[0] = '.';
   2179         appDir[1] = '/';
   2180     }
   2181 #elif defined(__APPLE__)
   2182     uint32_t size = sizeof(appDir);
   2183 
   2184     if (_NSGetExecutablePath(appDir, &size) == 0)
   2185     {
   2186         int len = strlen(appDir);
   2187         for (int i = len; i >= 0; --i)
   2188         {
   2189             if (appDir[i] == '/')
   2190             {
   2191                 appDir[i + 1] = '\0';
   2192                 break;
   2193             }
   2194         }
   2195     }
   2196     else
   2197     {
   2198         appDir[0] = '.';
   2199         appDir[1] = '/';
   2200     }
   2201 #elif defined(__FreeBSD__)
   2202     size_t size = sizeof(appDir);
   2203     int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
   2204 
   2205     if (sysctl(mib, 4, appDir, &size, NULL, 0) == 0)
   2206     {
   2207         int len = strlen(appDir);
   2208         for (int i = len; i >= 0; --i)
   2209         {
   2210             if (appDir[i] == '/')
   2211             {
   2212                 appDir[i + 1] = '\0';
   2213                 break;
   2214             }
   2215         }
   2216     }
   2217     else
   2218     {
   2219         appDir[0] = '.';
   2220         appDir[1] = '/';
   2221     }
   2222 
   2223 #endif
   2224 
   2225     return appDir;
   2226 }
   2227 
   2228 // Load directory filepaths
   2229 // NOTE: Base path is prepended to the scanned filepaths
   2230 // WARNING: Directory is scanned twice, first time to get files count
   2231 // No recursive scanning is done!
   2232 FilePathList LoadDirectoryFiles(const char *dirPath)
   2233 {
   2234     FilePathList files = { 0 };
   2235     unsigned int fileCounter = 0;
   2236 
   2237     struct dirent *entity;
   2238     DIR *dir = opendir(dirPath);
   2239 
   2240     if (dir != NULL) // It's a directory
   2241     {
   2242         // SCAN 1: Count files
   2243         while ((entity = readdir(dir)) != NULL)
   2244         {
   2245             // NOTE: We skip '.' (current dir) and '..' (parent dir) filepaths
   2246             if ((strcmp(entity->d_name, ".") != 0) && (strcmp(entity->d_name, "..") != 0)) fileCounter++;
   2247         }
   2248 
   2249         // Memory allocation for dirFileCount
   2250         files.capacity = fileCounter;
   2251         files.paths = (char **)RL_MALLOC(files.capacity*sizeof(char *));
   2252         for (unsigned int i = 0; i < files.capacity; i++) files.paths[i] = (char *)RL_MALLOC(MAX_FILEPATH_LENGTH*sizeof(char));
   2253 
   2254         closedir(dir);
   2255 
   2256         // SCAN 2: Read filepaths
   2257         // NOTE: Directory paths are also registered
   2258         ScanDirectoryFiles(dirPath, &files, NULL);
   2259 
   2260         // Security check: read files.count should match fileCounter
   2261         if (files.count != files.capacity) TRACELOG(LOG_WARNING, "FILEIO: Read files count do not match capacity allocated");
   2262     }
   2263     else TRACELOG(LOG_WARNING, "FILEIO: Failed to open requested directory");  // Maybe it's a file...
   2264 
   2265     return files;
   2266 }
   2267 
   2268 // Load directory filepaths with extension filtering and recursive directory scan
   2269 // NOTE: On recursive loading we do not pre-scan for file count, we use MAX_FILEPATH_CAPACITY
   2270 FilePathList LoadDirectoryFilesEx(const char *basePath, const char *filter, bool scanSubdirs)
   2271 {
   2272     FilePathList files = { 0 };
   2273 
   2274     files.capacity = MAX_FILEPATH_CAPACITY;
   2275     files.paths = (char **)RL_CALLOC(files.capacity, sizeof(char *));
   2276     for (unsigned int i = 0; i < files.capacity; i++) files.paths[i] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char));
   2277 
   2278     // WARNING: basePath is always prepended to scanned paths
   2279     if (scanSubdirs) ScanDirectoryFilesRecursively(basePath, &files, filter);
   2280     else ScanDirectoryFiles(basePath, &files, filter);
   2281 
   2282     return files;
   2283 }
   2284 
   2285 // Unload directory filepaths
   2286 // WARNING: files.count is not reseted to 0 after unloading
   2287 void UnloadDirectoryFiles(FilePathList files)
   2288 {
   2289     for (unsigned int i = 0; i < files.capacity; i++) RL_FREE(files.paths[i]);
   2290 
   2291     RL_FREE(files.paths);
   2292 }
   2293 
   2294 // Create directories (including full path requested), returns 0 on success
   2295 int MakeDirectory(const char *dirPath)
   2296 {
   2297     if ((dirPath == NULL) || (dirPath[0] == '\0')) return 1; // Path is not valid
   2298     if (DirectoryExists(dirPath)) return 0; // Path already exists (is valid)
   2299 
   2300     // Copy path string to avoid modifying original
   2301     int len = (int)strlen(dirPath) + 1;
   2302     char *pathcpy = (char *)RL_CALLOC(len, 1);
   2303     memcpy(pathcpy, dirPath, len);
   2304 
   2305     // Iterate over pathcpy, create each subdirectory as needed
   2306     for (int i = 0; (i < len) && (pathcpy[i] != '\0'); i++)
   2307     {
   2308         if (pathcpy[i] == ':') i++;
   2309         else
   2310         {
   2311             if ((pathcpy[i] == '\\') || (pathcpy[i] == '/'))
   2312             {
   2313                 pathcpy[i] = '\0';
   2314                 if (!DirectoryExists(pathcpy)) MKDIR(pathcpy);
   2315                 pathcpy[i] = '/';
   2316             }
   2317         }
   2318     }
   2319 
   2320     // Create final directory
   2321     if (!DirectoryExists(pathcpy)) MKDIR(pathcpy);
   2322 
   2323     RL_FREE(pathcpy);
   2324 
   2325     return 0;
   2326 }
   2327 
   2328 // Change working directory, returns true on success
   2329 bool ChangeDirectory(const char *dir)
   2330 {
   2331     bool result = CHDIR(dir);
   2332 
   2333     if (result != 0) TRACELOG(LOG_WARNING, "SYSTEM: Failed to change to directory: %s", dir);
   2334 
   2335     return (result == 0);
   2336 }
   2337 
   2338 // Check if a given path point to a file
   2339 bool IsPathFile(const char *path)
   2340 {
   2341     struct stat result = { 0 };
   2342     stat(path, &result);
   2343 
   2344     return S_ISREG(result.st_mode);
   2345 }
   2346 
   2347 // Check if fileName is valid for the platform/OS
   2348 bool IsFileNameValid(const char *fileName)
   2349 {
   2350     bool valid = true;
   2351 
   2352     if ((fileName != NULL) && (fileName[0] != '\0'))
   2353     {
   2354         int length = (int)strlen(fileName);
   2355         bool allPeriods = true;
   2356 
   2357         for (int i = 0; i < length; i++)
   2358         {
   2359             // Check invalid characters
   2360             if ((fileName[i] == '<') ||
   2361                 (fileName[i] == '>') ||
   2362                 (fileName[i] == ':') ||
   2363                 (fileName[i] == '\"') ||
   2364                 (fileName[i] == '/') ||
   2365                 (fileName[i] == '\\') ||
   2366                 (fileName[i] == '|') ||
   2367                 (fileName[i] == '?') ||
   2368                 (fileName[i] == '*')) { valid = false; break; }
   2369 
   2370             // Check non-glyph characters
   2371             if ((unsigned char)fileName[i] < 32) { valid = false; break; }
   2372 
   2373             // TODO: Check trailing periods/spaces?
   2374 
   2375             // Check if filename is not all periods
   2376             if (fileName[i] != '.') allPeriods = false;
   2377         }
   2378 
   2379         if (allPeriods) valid = false;
   2380 
   2381 /*
   2382         if (valid)
   2383         {
   2384             // Check invalid DOS names
   2385             if (length >= 3)
   2386             {
   2387                 if (((fileName[0] == 'C') && (fileName[1] == 'O') && (fileName[2] == 'N')) ||   // CON
   2388                     ((fileName[0] == 'P') && (fileName[1] == 'R') && (fileName[2] == 'N')) ||   // PRN
   2389                     ((fileName[0] == 'A') && (fileName[1] == 'U') && (fileName[2] == 'X')) ||   // AUX
   2390                     ((fileName[0] == 'N') && (fileName[1] == 'U') && (fileName[2] == 'L'))) valid = false; // NUL
   2391             }
   2392 
   2393             if (length >= 4)
   2394             {
   2395                 if (((fileName[0] == 'C') && (fileName[1] == 'O') && (fileName[2] == 'M') && ((fileName[3] >= '0') && (fileName[3] <= '9'))) ||  // COM0-9
   2396                     ((fileName[0] == 'L') && (fileName[1] == 'P') && (fileName[2] == 'T') && ((fileName[3] >= '0') && (fileName[3] <= '9')))) valid = false; // LPT0-9
   2397             }
   2398         }
   2399 */
   2400     }
   2401 
   2402     return valid;
   2403 }
   2404 
   2405 // Check if a file has been dropped into window
   2406 bool IsFileDropped(void)
   2407 {
   2408     bool result = false;
   2409 
   2410     if (CORE.Window.dropFileCount > 0) result = true;
   2411 
   2412     return result;
   2413 }
   2414 
   2415 // Load dropped filepaths
   2416 FilePathList LoadDroppedFiles(void)
   2417 {
   2418     FilePathList files = { 0 };
   2419 
   2420     files.count = CORE.Window.dropFileCount;
   2421     files.paths = CORE.Window.dropFilepaths;
   2422 
   2423     return files;
   2424 }
   2425 
   2426 // Unload dropped filepaths
   2427 void UnloadDroppedFiles(FilePathList files)
   2428 {
   2429     // WARNING: files pointers are the same as internal ones
   2430 
   2431     if (files.count > 0)
   2432     {
   2433         for (unsigned int i = 0; i < files.count; i++) RL_FREE(files.paths[i]);
   2434 
   2435         RL_FREE(files.paths);
   2436 
   2437         CORE.Window.dropFileCount = 0;
   2438         CORE.Window.dropFilepaths = NULL;
   2439     }
   2440 }
   2441 
   2442 // Get file modification time (last write time)
   2443 long GetFileModTime(const char *fileName)
   2444 {
   2445     struct stat result = { 0 };
   2446     long modTime = 0;
   2447 
   2448     if (stat(fileName, &result) == 0)
   2449     {
   2450         time_t mod = result.st_mtime;
   2451 
   2452         modTime = (long)mod;
   2453     }
   2454 
   2455     return modTime;
   2456 }
   2457 
   2458 //----------------------------------------------------------------------------------
   2459 // Module Functions Definition: Compression and Encoding
   2460 //----------------------------------------------------------------------------------
   2461 
   2462 // Compress data (DEFLATE algorithm)
   2463 unsigned char *CompressData(const unsigned char *data, int dataSize, int *compDataSize)
   2464 {
   2465     #define COMPRESSION_QUALITY_DEFLATE  8
   2466 
   2467     unsigned char *compData = NULL;
   2468 
   2469 #if defined(SUPPORT_COMPRESSION_API)
   2470     // Compress data and generate a valid DEFLATE stream
   2471     struct sdefl *sdefl = RL_CALLOC(1, sizeof(struct sdefl));   // WARNING: Possible stack overflow, struct sdefl is almost 1MB
   2472     int bounds = sdefl_bound(dataSize);
   2473     compData = (unsigned char *)RL_CALLOC(bounds, 1);
   2474 
   2475     *compDataSize = sdeflate(sdefl, compData, data, dataSize, COMPRESSION_QUALITY_DEFLATE);   // Compression level 8, same as stbiw
   2476     RL_FREE(sdefl);
   2477 
   2478     TRACELOG(LOG_INFO, "SYSTEM: Compress data: Original size: %i -> Comp. size: %i", dataSize, *compDataSize);
   2479 #endif
   2480 
   2481     return compData;
   2482 }
   2483 
   2484 // Decompress data (DEFLATE algorithm)
   2485 unsigned char *DecompressData(const unsigned char *compData, int compDataSize, int *dataSize)
   2486 {
   2487     unsigned char *data = NULL;
   2488 
   2489 #if defined(SUPPORT_COMPRESSION_API)
   2490     // Decompress data from a valid DEFLATE stream
   2491     data = (unsigned char *)RL_CALLOC(MAX_DECOMPRESSION_SIZE*1024*1024, 1);
   2492     int length = sinflate(data, MAX_DECOMPRESSION_SIZE*1024*1024, compData, compDataSize);
   2493 
   2494     // WARNING: RL_REALLOC can make (and leave) data copies in memory, be careful with sensitive compressed data!
   2495     // TODO: Use a different approach, create another buffer, copy data manually to it and wipe original buffer memory
   2496     unsigned char *temp = (unsigned char *)RL_REALLOC(data, length);
   2497 
   2498     if (temp != NULL) data = temp;
   2499     else TRACELOG(LOG_WARNING, "SYSTEM: Failed to re-allocate required decompression memory");
   2500 
   2501     *dataSize = length;
   2502 
   2503     TRACELOG(LOG_INFO, "SYSTEM: Decompress data: Comp. size: %i -> Original size: %i", compDataSize, *dataSize);
   2504 #endif
   2505 
   2506     return data;
   2507 }
   2508 
   2509 // Encode data to Base64 string
   2510 char *EncodeDataBase64(const unsigned char *data, int dataSize, int *outputSize)
   2511 {
   2512     static const unsigned char base64encodeTable[] = {
   2513         'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
   2514         'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
   2515         'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
   2516     };
   2517 
   2518     static const int modTable[] = { 0, 2, 1 };
   2519 
   2520     *outputSize = 4*((dataSize + 2)/3);
   2521 
   2522     char *encodedData = (char *)RL_MALLOC(*outputSize);
   2523 
   2524     if (encodedData == NULL) return NULL;   // Security check
   2525 
   2526     for (int i = 0, j = 0; i < dataSize;)
   2527     {
   2528         unsigned int octetA = (i < dataSize)? (unsigned char)data[i++] : 0;
   2529         unsigned int octetB = (i < dataSize)? (unsigned char)data[i++] : 0;
   2530         unsigned int octetC = (i < dataSize)? (unsigned char)data[i++] : 0;
   2531 
   2532         unsigned int triple = (octetA << 0x10) + (octetB << 0x08) + octetC;
   2533 
   2534         encodedData[j++] = base64encodeTable[(triple >> 3*6) & 0x3F];
   2535         encodedData[j++] = base64encodeTable[(triple >> 2*6) & 0x3F];
   2536         encodedData[j++] = base64encodeTable[(triple >> 1*6) & 0x3F];
   2537         encodedData[j++] = base64encodeTable[(triple >> 0*6) & 0x3F];
   2538     }
   2539 
   2540     for (int i = 0; i < modTable[dataSize%3]; i++) encodedData[*outputSize - 1 - i] = '=';  // Padding character
   2541 
   2542     return encodedData;
   2543 }
   2544 
   2545 // Decode Base64 string data
   2546 unsigned char *DecodeDataBase64(const unsigned char *data, int *outputSize)
   2547 {
   2548     static const unsigned char base64decodeTable[] = {
   2549         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
   2550         0, 0, 0, 62, 0, 0, 0, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
   2551         11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
   2552         37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
   2553     };
   2554 
   2555     // Get output size of Base64 input data
   2556     int outSize = 0;
   2557     for (int i = 0; data[4*i] != 0; i++)
   2558     {
   2559         if (data[4*i + 3] == '=')
   2560         {
   2561             if (data[4*i + 2] == '=') outSize += 1;
   2562             else outSize += 2;
   2563         }
   2564         else outSize += 3;
   2565     }
   2566 
   2567     // Allocate memory to store decoded Base64 data
   2568     unsigned char *decodedData = (unsigned char *)RL_MALLOC(outSize);
   2569 
   2570     for (int i = 0; i < outSize/3; i++)
   2571     {
   2572         unsigned char a = base64decodeTable[(int)data[4*i]];
   2573         unsigned char b = base64decodeTable[(int)data[4*i + 1]];
   2574         unsigned char c = base64decodeTable[(int)data[4*i + 2]];
   2575         unsigned char d = base64decodeTable[(int)data[4*i + 3]];
   2576 
   2577         decodedData[3*i] = (a << 2) | (b >> 4);
   2578         decodedData[3*i + 1] = (b << 4) | (c >> 2);
   2579         decodedData[3*i + 2] = (c << 6) | d;
   2580     }
   2581 
   2582     if (outSize%3 == 1)
   2583     {
   2584         int n = outSize/3;
   2585         unsigned char a = base64decodeTable[(int)data[4*n]];
   2586         unsigned char b = base64decodeTable[(int)data[4*n + 1]];
   2587         decodedData[outSize - 1] = (a << 2) | (b >> 4);
   2588     }
   2589     else if (outSize%3 == 2)
   2590     {
   2591         int n = outSize/3;
   2592         unsigned char a = base64decodeTable[(int)data[4*n]];
   2593         unsigned char b = base64decodeTable[(int)data[4*n + 1]];
   2594         unsigned char c = base64decodeTable[(int)data[4*n + 2]];
   2595         decodedData[outSize - 2] = (a << 2) | (b >> 4);
   2596         decodedData[outSize - 1] = (b << 4) | (c >> 2);
   2597     }
   2598 
   2599     *outputSize = outSize;
   2600     return decodedData;
   2601 }
   2602 
   2603 // Compute CRC32 hash code
   2604 unsigned int ComputeCRC32(unsigned char *data, int dataSize)
   2605 {
   2606     static unsigned int crcTable[256] = {
   2607         0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
   2608         0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
   2609         0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
   2610         0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
   2611         0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
   2612         0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
   2613         0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
   2614         0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
   2615         0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
   2616         0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
   2617         0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
   2618         0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
   2619         0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
   2620         0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
   2621         0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
   2622         0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
   2623         0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
   2624         0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
   2625         0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
   2626         0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
   2627         0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
   2628         0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
   2629         0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
   2630         0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
   2631         0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
   2632         0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
   2633         0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
   2634         0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
   2635         0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
   2636         0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
   2637         0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
   2638         0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
   2639     };
   2640 
   2641     unsigned int crc = ~0u;
   2642 
   2643     for (int i = 0; i < dataSize; i++) crc = (crc >> 8) ^ crcTable[data[i] ^ (crc & 0xff)];
   2644 
   2645     return ~crc;
   2646 }
   2647 
   2648 // Compute MD5 hash code
   2649 // NOTE: Returns a static int[4] array (16 bytes)
   2650 unsigned int *ComputeMD5(unsigned char *data, int dataSize)
   2651 {
   2652     #define ROTATE_LEFT(x, c) (((x) << (c)) | ((x) >> (32 - (c))))
   2653 
   2654     static unsigned int hash[4] = { 0 };  // Hash to be returned
   2655 
   2656     // WARNING: All variables are unsigned 32 bit and wrap modulo 2^32 when calculating
   2657 
   2658     // NOTE: r specifies the per-round shift amounts
   2659     unsigned int r[] = {
   2660         7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
   2661         5,  9, 14, 20, 5,  9, 14, 20, 5,  9, 14, 20, 5,  9, 14, 20,
   2662         4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
   2663         6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21
   2664     };
   2665 
   2666     // Using binary integer part of the sines of integers (in radians) as constants
   2667     unsigned int k[] = {
   2668         0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
   2669         0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
   2670         0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
   2671         0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
   2672         0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
   2673         0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
   2674         0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
   2675         0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
   2676         0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
   2677         0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
   2678         0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
   2679         0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
   2680         0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
   2681         0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
   2682         0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
   2683         0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
   2684     };
   2685 
   2686     hash[0] = 0x67452301;
   2687     hash[1] = 0xefcdab89;
   2688     hash[2] = 0x98badcfe;
   2689     hash[3] = 0x10325476;
   2690 
   2691     // Pre-processing: adding a single 1 bit
   2692     // Append '1' bit to message
   2693     // NOTE: The input bytes are considered as bits strings,
   2694     // where the first bit is the most significant bit of the byte
   2695 
   2696     // Pre-processing: padding with zeros
   2697     // Append '0' bit until message length in bit 448 (mod 512)
   2698     // Append length mod (2 pow 64) to message
   2699 
   2700     int newDataSize = ((((dataSize + 8)/64) + 1)*64) - 8;
   2701 
   2702     unsigned char *msg = RL_CALLOC(newDataSize + 64, 1); // Initialize with '0' bits, allocating 64 extra bytes
   2703     memcpy(msg, data, dataSize);
   2704     msg[dataSize] = 128; // Write the '1' bit
   2705 
   2706     unsigned int bitsLen = 8*dataSize;
   2707     memcpy(msg + newDataSize, &bitsLen, 4); // Append the len in bits at the end of the buffer
   2708 
   2709     // Process the message in successive 512-bit chunks for each 512-bit chunk of message
   2710     for (int offset = 0; offset < newDataSize; offset += (512/8))
   2711     {
   2712         // Break chunk into sixteen 32-bit words w[j], 0 <= j <= 15
   2713         unsigned int *w = (unsigned int *)(msg + offset);
   2714 
   2715         // Initialize hash value for this chunk
   2716         unsigned int a = hash[0];
   2717         unsigned int b = hash[1];
   2718         unsigned int c = hash[2];
   2719         unsigned int d = hash[3];
   2720 
   2721         for (int i = 0; i < 64; i++)
   2722         {
   2723             unsigned int f = 0;
   2724             unsigned int g = 0;
   2725 
   2726             if (i < 16)
   2727             {
   2728                 f = (b & c) | ((~b) & d);
   2729                 g = i;
   2730             }
   2731             else if (i < 32)
   2732             {
   2733                 f = (d & b) | ((~d) & c);
   2734                 g = (5*i + 1)%16;
   2735             }
   2736             else if (i < 48)
   2737             {
   2738                 f = b ^ c ^ d;
   2739                 g = (3*i + 5)%16;
   2740             }
   2741             else
   2742             {
   2743                 f = c ^ (b | (~d));
   2744                 g = (7*i)%16;
   2745             }
   2746 
   2747             unsigned int temp = d;
   2748             d = c;
   2749             c = b;
   2750             b = b + ROTATE_LEFT((a + f + k[i] + w[g]), r[i]);
   2751             a = temp;
   2752         }
   2753 
   2754         // Add chunk's hash to result so far
   2755         hash[0] += a;
   2756         hash[1] += b;
   2757         hash[2] += c;
   2758         hash[3] += d;
   2759     }
   2760 
   2761     RL_FREE(msg);
   2762 
   2763     return hash;
   2764 }
   2765 
   2766 // Compute SHA-1 hash code
   2767 // NOTE: Returns a static int[5] array (20 bytes)
   2768 unsigned int *ComputeSHA1(unsigned char *data, int dataSize) {
   2769     #define ROTATE_LEFT(x, c) (((x) << (c)) | ((x) >> (32 - (c))))
   2770 
   2771     static unsigned int hash[5] = { 0 };  // Hash to be returned
   2772 
   2773     // Initialize hash values
   2774     hash[0] = 0x67452301;
   2775     hash[1] = 0xEFCDAB89;
   2776     hash[2] = 0x98BADCFE;
   2777     hash[3] = 0x10325476;
   2778     hash[4] = 0xC3D2E1F0;
   2779 
   2780     // Pre-processing: adding a single 1 bit
   2781     // Append '1' bit to message
   2782     // NOTE: The input bytes are considered as bits strings,
   2783     // where the first bit is the most significant bit of the byte
   2784 
   2785     // Pre-processing: padding with zeros
   2786     // Append '0' bit until message length in bit 448 (mod 512)
   2787     // Append length mod (2 pow 64) to message
   2788 
   2789     int newDataSize = ((((dataSize + 8)/64) + 1)*64);
   2790 
   2791     unsigned char *msg = RL_CALLOC(newDataSize, 1); // Initialize with '0' bits
   2792     memcpy(msg, data, dataSize);
   2793     msg[dataSize] = 128; // Write the '1' bit
   2794 
   2795     unsigned int bitsLen = 8*dataSize;
   2796     msg[newDataSize-1] = bitsLen;
   2797 
   2798     // Process the message in successive 512-bit chunks
   2799     for (int offset = 0; offset < newDataSize; offset += (512/8))
   2800     {
   2801         // Break chunk into sixteen 32-bit words w[j], 0 <= j <= 15
   2802         unsigned int w[80] = {0};
   2803         for (int i = 0; i < 16; i++) {
   2804             w[i] = (msg[offset + (i * 4) + 0] << 24) |
   2805                    (msg[offset + (i * 4) + 1] << 16) |
   2806                    (msg[offset + (i * 4) + 2] << 8) |
   2807                    (msg[offset + (i * 4) + 3]);
   2808         }
   2809 
   2810         // Message schedule: extend the sixteen 32-bit words into eighty 32-bit words:
   2811         for (int i = 16; i < 80; ++i) {
   2812             w[i] = ROTATE_LEFT(w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16], 1);
   2813         }
   2814 
   2815         // Initialize hash value for this chunk
   2816         unsigned int a = hash[0];
   2817         unsigned int b = hash[1];
   2818         unsigned int c = hash[2];
   2819         unsigned int d = hash[3];
   2820         unsigned int e = hash[4];
   2821 
   2822         for (int i = 0; i < 80; i++)
   2823         {
   2824             unsigned int f = 0;
   2825             unsigned int k = 0;
   2826 
   2827             if (i < 20) {
   2828                 f = (b & c) | ((~b) & d);
   2829                 k = 0x5A827999;
   2830             } else if (i < 40) {
   2831                 f = b ^ c ^ d;
   2832                 k = 0x6ED9EBA1;
   2833             } else if (i < 60) {
   2834                 f = (b & c) | (b & d) | (c & d);
   2835                 k = 0x8F1BBCDC;
   2836             } else {
   2837                 f = b ^ c ^ d;
   2838                 k = 0xCA62C1D6;
   2839             }
   2840 
   2841             unsigned int temp = ROTATE_LEFT(a, 5) + f + e + k + w[i];
   2842             e = d;
   2843             d = c;
   2844             c = ROTATE_LEFT(b, 30);
   2845             b = a;
   2846             a = temp;
   2847         }
   2848 
   2849         // Add this chunk's hash to result so far
   2850         hash[0] += a;
   2851         hash[1] += b;
   2852         hash[2] += c;
   2853         hash[3] += d;
   2854         hash[4] += e;
   2855     }
   2856 
   2857     free(msg);
   2858 
   2859     return hash;
   2860 }
   2861 
   2862 //----------------------------------------------------------------------------------
   2863 // Module Functions Definition: Automation Events Recording and Playing
   2864 //----------------------------------------------------------------------------------
   2865 
   2866 // Load automation events list from file, NULL for empty list, capacity = MAX_AUTOMATION_EVENTS
   2867 AutomationEventList LoadAutomationEventList(const char *fileName)
   2868 {
   2869     AutomationEventList list = { 0 };
   2870 
   2871     // Allocate and empty automation event list, ready to record new events
   2872     list.events = (AutomationEvent *)RL_CALLOC(MAX_AUTOMATION_EVENTS, sizeof(AutomationEvent));
   2873     list.capacity = MAX_AUTOMATION_EVENTS;
   2874 
   2875 #if defined(SUPPORT_AUTOMATION_EVENTS)
   2876     if (fileName == NULL) TRACELOG(LOG_INFO, "AUTOMATION: New empty events list loaded successfully");
   2877     else
   2878     {
   2879         // Load automation events file (binary)
   2880         /*
   2881         //int dataSize = 0;
   2882         //unsigned char *data = LoadFileData(fileName, &dataSize);
   2883 
   2884         FILE *raeFile = fopen(fileName, "rb");
   2885         unsigned char fileId[4] = { 0 };
   2886 
   2887         fread(fileId, 1, 4, raeFile);
   2888 
   2889         if ((fileId[0] == 'r') && (fileId[1] == 'A') && (fileId[2] == 'E') && (fileId[1] == ' '))
   2890         {
   2891             fread(&eventCount, sizeof(int), 1, raeFile);
   2892             TRACELOG(LOG_WARNING, "Events loaded: %i\n", eventCount);
   2893             fread(events, sizeof(AutomationEvent), eventCount, raeFile);
   2894         }
   2895 
   2896         fclose(raeFile);
   2897         */
   2898 
   2899         // Load events file (text)
   2900         //unsigned char *buffer = LoadFileText(fileName);
   2901         FILE *raeFile = fopen(fileName, "rt");
   2902 
   2903         if (raeFile != NULL)
   2904         {
   2905             unsigned int counter = 0;
   2906             char buffer[256] = { 0 };
   2907             char eventDesc[64] = { 0 };
   2908 
   2909             fgets(buffer, 256, raeFile);
   2910 
   2911             while (!feof(raeFile))
   2912             {
   2913                 switch (buffer[0])
   2914                 {
   2915                     case 'c': sscanf(buffer, "c %i", &list.count); break;
   2916                     case 'e':
   2917                     {
   2918                         sscanf(buffer, "e %d %d %d %d %d %d %[^\n]s", &list.events[counter].frame, &list.events[counter].type,
   2919                                &list.events[counter].params[0], &list.events[counter].params[1], &list.events[counter].params[2], &list.events[counter].params[3], eventDesc);
   2920 
   2921                         counter++;
   2922                     } break;
   2923                     default: break;
   2924                 }
   2925 
   2926                 fgets(buffer, 256, raeFile);
   2927             }
   2928 
   2929             if (counter != list.count)
   2930             {
   2931                 TRACELOG(LOG_WARNING, "AUTOMATION: Events read from file [%i] do not mach event count specified [%i]", counter, list.count);
   2932                 list.count = counter;
   2933             }
   2934 
   2935             fclose(raeFile);
   2936 
   2937             TRACELOG(LOG_INFO, "AUTOMATION: Events file loaded successfully");
   2938         }
   2939 
   2940         TRACELOG(LOG_INFO, "AUTOMATION: Events loaded from file: %i", list.count);
   2941     }
   2942 #endif
   2943     return list;
   2944 }
   2945 
   2946 // Unload automation events list from file
   2947 void UnloadAutomationEventList(AutomationEventList list)
   2948 {
   2949 #if defined(SUPPORT_AUTOMATION_EVENTS)
   2950     RL_FREE(list.events);
   2951 #endif
   2952 }
   2953 
   2954 // Export automation events list as text file
   2955 bool ExportAutomationEventList(AutomationEventList list, const char *fileName)
   2956 {
   2957     bool success = false;
   2958 
   2959 #if defined(SUPPORT_AUTOMATION_EVENTS)
   2960     // Export events as binary file
   2961     // TODO: Save to memory buffer and SaveFileData()
   2962     /*
   2963     unsigned char fileId[4] = "rAE ";
   2964     FILE *raeFile = fopen(fileName, "wb");
   2965     fwrite(fileId, sizeof(unsigned char), 4, raeFile);
   2966     fwrite(&eventCount, sizeof(int), 1, raeFile);
   2967     fwrite(events, sizeof(AutomationEvent), eventCount, raeFile);
   2968     fclose(raeFile);
   2969     */
   2970 
   2971     // Export events as text
   2972     // TODO: Save to memory buffer and SaveFileText()
   2973     char *txtData = (char *)RL_CALLOC(256*list.count + 2048, sizeof(char)); // 256 characters per line plus some header
   2974 
   2975     int byteCount = 0;
   2976     byteCount += sprintf(txtData + byteCount, "#\n");
   2977     byteCount += sprintf(txtData + byteCount, "# Automation events exporter v1.0 - raylib automation events list\n");
   2978     byteCount += sprintf(txtData + byteCount, "#\n");
   2979     byteCount += sprintf(txtData + byteCount, "#    c <events_count>\n");
   2980     byteCount += sprintf(txtData + byteCount, "#    e <frame> <event_type> <param0> <param1> <param2> <param3> // <event_type_name>\n");
   2981     byteCount += sprintf(txtData + byteCount, "#\n");
   2982     byteCount += sprintf(txtData + byteCount, "# more info and bugs-report:  github.com/raysan5/raylib\n");
   2983     byteCount += sprintf(txtData + byteCount, "# feedback and support:       ray[at]raylib.com\n");
   2984     byteCount += sprintf(txtData + byteCount, "#\n");
   2985     byteCount += sprintf(txtData + byteCount, "# Copyright (c) 2023-2024 Ramon Santamaria (@raysan5)\n");
   2986     byteCount += sprintf(txtData + byteCount, "#\n\n");
   2987 
   2988     // Add events data
   2989     byteCount += sprintf(txtData + byteCount, "c %i\n", list.count);
   2990     for (unsigned int i = 0; i < list.count; i++)
   2991     {
   2992         byteCount += snprintf(txtData + byteCount, 256, "e %i %i %i %i %i %i // Event: %s\n", list.events[i].frame, list.events[i].type,
   2993             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]);
   2994     }
   2995 
   2996     // NOTE: Text data size exported is determined by '\0' (NULL) character
   2997     success = SaveFileText(fileName, txtData);
   2998 
   2999     RL_FREE(txtData);
   3000 #endif
   3001 
   3002     return success;
   3003 }
   3004 
   3005 // Setup automation event list to record to
   3006 void SetAutomationEventList(AutomationEventList *list)
   3007 {
   3008 #if defined(SUPPORT_AUTOMATION_EVENTS)
   3009     currentEventList = list;
   3010 #endif
   3011 }
   3012 
   3013 // Set automation event internal base frame to start recording
   3014 void SetAutomationEventBaseFrame(int frame)
   3015 {
   3016     CORE.Time.frameCounter = frame;
   3017 }
   3018 
   3019 // Start recording automation events (AutomationEventList must be set)
   3020 void StartAutomationEventRecording(void)
   3021 {
   3022 #if defined(SUPPORT_AUTOMATION_EVENTS)
   3023     automationEventRecording = true;
   3024 #endif
   3025 }
   3026 
   3027 // Stop recording automation events
   3028 void StopAutomationEventRecording(void)
   3029 {
   3030 #if defined(SUPPORT_AUTOMATION_EVENTS)
   3031     automationEventRecording = false;
   3032 #endif
   3033 }
   3034 
   3035 // Play a recorded automation event
   3036 void PlayAutomationEvent(AutomationEvent event)
   3037 {
   3038 #if defined(SUPPORT_AUTOMATION_EVENTS)
   3039     // WARNING: When should event be played? After/before/replace PollInputEvents()? -> Up to the user!
   3040 
   3041     if (!automationEventRecording)      // TODO: Allow recording events while playing?
   3042     {
   3043         switch (event.type)
   3044         {
   3045             // Input event
   3046             case INPUT_KEY_UP: CORE.Input.Keyboard.currentKeyState[event.params[0]] = false; break;             // param[0]: key
   3047             case INPUT_KEY_DOWN: {                                                                              // param[0]: key
   3048                 CORE.Input.Keyboard.currentKeyState[event.params[0]] = true;
   3049 
   3050                 if (CORE.Input.Keyboard.previousKeyState[event.params[0]] == false)
   3051                 {
   3052                     if (CORE.Input.Keyboard.keyPressedQueueCount < MAX_KEY_PRESSED_QUEUE)
   3053                     {
   3054                         // Add character to the queue
   3055                         CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = event.params[0];
   3056                         CORE.Input.Keyboard.keyPressedQueueCount++;
   3057                     }
   3058                 }
   3059             } break;
   3060             case INPUT_MOUSE_BUTTON_UP: CORE.Input.Mouse.currentButtonState[event.params[0]] = false; break;    // param[0]: key
   3061             case INPUT_MOUSE_BUTTON_DOWN: CORE.Input.Mouse.currentButtonState[event.params[0]] = true; break;   // param[0]: key
   3062             case INPUT_MOUSE_POSITION:      // param[0]: x, param[1]: y
   3063             {
   3064                 CORE.Input.Mouse.currentPosition.x = (float)event.params[0];
   3065                 CORE.Input.Mouse.currentPosition.y = (float)event.params[1];
   3066             } break;
   3067             case INPUT_MOUSE_WHEEL_MOTION:  // param[0]: x delta, param[1]: y delta
   3068             {
   3069                 CORE.Input.Mouse.currentWheelMove.x = (float)event.params[0];
   3070                 CORE.Input.Mouse.currentWheelMove.y = (float)event.params[1];
   3071             } break;
   3072             case INPUT_TOUCH_UP: CORE.Input.Touch.currentTouchState[event.params[0]] = false; break;            // param[0]: id
   3073             case INPUT_TOUCH_DOWN: CORE.Input.Touch.currentTouchState[event.params[0]] = true; break;           // param[0]: id
   3074             case INPUT_TOUCH_POSITION:      // param[0]: id, param[1]: x, param[2]: y
   3075             {
   3076                 CORE.Input.Touch.position[event.params[0]].x = (float)event.params[1];
   3077                 CORE.Input.Touch.position[event.params[0]].y = (float)event.params[2];
   3078             } break;
   3079             case INPUT_GAMEPAD_CONNECT: CORE.Input.Gamepad.ready[event.params[0]] = true; break;                // param[0]: gamepad
   3080             case INPUT_GAMEPAD_DISCONNECT: CORE.Input.Gamepad.ready[event.params[0]] = false; break;            // param[0]: gamepad
   3081             case INPUT_GAMEPAD_BUTTON_UP: CORE.Input.Gamepad.currentButtonState[event.params[0]][event.params[1]] = false; break;    // param[0]: gamepad, param[1]: button
   3082             case INPUT_GAMEPAD_BUTTON_DOWN: CORE.Input.Gamepad.currentButtonState[event.params[0]][event.params[1]] = true; break;   // param[0]: gamepad, param[1]: button
   3083             case INPUT_GAMEPAD_AXIS_MOTION: // param[0]: gamepad, param[1]: axis, param[2]: delta
   3084             {
   3085                 CORE.Input.Gamepad.axisState[event.params[0]][event.params[1]] = ((float)event.params[2]/32768.0f);
   3086             } break;
   3087     #if defined(SUPPORT_GESTURES_SYSTEM)
   3088             case INPUT_GESTURE: GESTURES.current = event.params[0]; break;     // param[0]: gesture (enum Gesture) -> rgestures.h: GESTURES.current
   3089     #endif
   3090             // Window event
   3091             case WINDOW_CLOSE: CORE.Window.shouldClose = true; break;
   3092             case WINDOW_MAXIMIZE: MaximizeWindow(); break;
   3093             case WINDOW_MINIMIZE: MinimizeWindow(); break;
   3094             case WINDOW_RESIZE: SetWindowSize(event.params[0], event.params[1]); break;
   3095 
   3096             // Custom event
   3097     #if defined(SUPPORT_SCREEN_CAPTURE)
   3098             case ACTION_TAKE_SCREENSHOT:
   3099             {
   3100                 TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter));
   3101                 screenshotCounter++;
   3102             } break;
   3103     #endif
   3104             case ACTION_SETTARGETFPS: SetTargetFPS(event.params[0]); break;
   3105             default: break;
   3106         }
   3107 
   3108         TRACELOG(LOG_INFO, "AUTOMATION PLAY: Frame: %i | Event type: %i | Event parameters: %i, %i, %i", event.frame, event.type, event.params[0], event.params[1], event.params[2]);
   3109     }
   3110 #endif
   3111 }
   3112 
   3113 //----------------------------------------------------------------------------------
   3114 // Module Functions Definition: Input Handling: Keyboard
   3115 //----------------------------------------------------------------------------------
   3116 
   3117 // Check if a key has been pressed once
   3118 bool IsKeyPressed(int key)
   3119 {
   3120 
   3121     bool pressed = false;
   3122 
   3123     if ((key > 0) && (key < MAX_KEYBOARD_KEYS))
   3124     {
   3125         if ((CORE.Input.Keyboard.previousKeyState[key] == 0) && (CORE.Input.Keyboard.currentKeyState[key] == 1)) pressed = true;
   3126     }
   3127 
   3128     return pressed;
   3129 }
   3130 
   3131 // Check if a key has been pressed again
   3132 bool IsKeyPressedRepeat(int key)
   3133 {
   3134     bool repeat = false;
   3135 
   3136     if ((key > 0) && (key < MAX_KEYBOARD_KEYS))
   3137     {
   3138         if (CORE.Input.Keyboard.keyRepeatInFrame[key] == 1) repeat = true;
   3139     }
   3140 
   3141     return repeat;
   3142 }
   3143 
   3144 // Check if a key is being pressed (key held down)
   3145 bool IsKeyDown(int key)
   3146 {
   3147     bool down = false;
   3148 
   3149     if ((key > 0) && (key < MAX_KEYBOARD_KEYS))
   3150     {
   3151         if (CORE.Input.Keyboard.currentKeyState[key] == 1) down = true;
   3152     }
   3153 
   3154     return down;
   3155 }
   3156 
   3157 // Check if a key has been released once
   3158 bool IsKeyReleased(int key)
   3159 {
   3160     bool released = false;
   3161 
   3162     if ((key > 0) && (key < MAX_KEYBOARD_KEYS))
   3163     {
   3164         if ((CORE.Input.Keyboard.previousKeyState[key] == 1) && (CORE.Input.Keyboard.currentKeyState[key] == 0)) released = true;
   3165     }
   3166 
   3167     return released;
   3168 }
   3169 
   3170 // Check if a key is NOT being pressed (key not held down)
   3171 bool IsKeyUp(int key)
   3172 {
   3173     bool up = false;
   3174 
   3175     if ((key > 0) && (key < MAX_KEYBOARD_KEYS))
   3176     {
   3177         if (CORE.Input.Keyboard.currentKeyState[key] == 0) up = true;
   3178     }
   3179 
   3180     return up;
   3181 }
   3182 
   3183 // Get the last key pressed
   3184 int GetKeyPressed(void)
   3185 {
   3186     int value = 0;
   3187 
   3188     if (CORE.Input.Keyboard.keyPressedQueueCount > 0)
   3189     {
   3190         // Get character from the queue head
   3191         value = CORE.Input.Keyboard.keyPressedQueue[0];
   3192 
   3193         // Shift elements 1 step toward the head
   3194         for (int i = 0; i < (CORE.Input.Keyboard.keyPressedQueueCount - 1); i++)
   3195             CORE.Input.Keyboard.keyPressedQueue[i] = CORE.Input.Keyboard.keyPressedQueue[i + 1];
   3196 
   3197         // Reset last character in the queue
   3198         CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount - 1] = 0;
   3199         CORE.Input.Keyboard.keyPressedQueueCount--;
   3200     }
   3201 
   3202     return value;
   3203 }
   3204 
   3205 // Get the last char pressed
   3206 int GetCharPressed(void)
   3207 {
   3208     int value = 0;
   3209 
   3210     if (CORE.Input.Keyboard.charPressedQueueCount > 0)
   3211     {
   3212         // Get character from the queue head
   3213         value = CORE.Input.Keyboard.charPressedQueue[0];
   3214 
   3215         // Shift elements 1 step toward the head
   3216         for (int i = 0; i < (CORE.Input.Keyboard.charPressedQueueCount - 1); i++)
   3217             CORE.Input.Keyboard.charPressedQueue[i] = CORE.Input.Keyboard.charPressedQueue[i + 1];
   3218 
   3219         // Reset last character in the queue
   3220         CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount - 1] = 0;
   3221         CORE.Input.Keyboard.charPressedQueueCount--;
   3222     }
   3223 
   3224     return value;
   3225 }
   3226 
   3227 // Set a custom key to exit program
   3228 // NOTE: default exitKey is set to ESCAPE
   3229 void SetExitKey(int key)
   3230 {
   3231     CORE.Input.Keyboard.exitKey = key;
   3232 }
   3233 
   3234 //----------------------------------------------------------------------------------
   3235 // Module Functions Definition: Input Handling: Gamepad
   3236 //----------------------------------------------------------------------------------
   3237 
   3238 // NOTE: Functions with a platform-specific implementation on rcore_<platform>.c
   3239 //int SetGamepadMappings(const char *mappings)
   3240 
   3241 // Check if a gamepad is available
   3242 bool IsGamepadAvailable(int gamepad)
   3243 {
   3244     bool result = false;
   3245 
   3246     if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad]) result = true;
   3247 
   3248     return result;
   3249 }
   3250 
   3251 // Get gamepad internal name id
   3252 const char *GetGamepadName(int gamepad)
   3253 {
   3254     return CORE.Input.Gamepad.name[gamepad];
   3255 }
   3256 
   3257 // Check if a gamepad button has been pressed once
   3258 bool IsGamepadButtonPressed(int gamepad, int button)
   3259 {
   3260     bool pressed = false;
   3261 
   3262     if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (button < MAX_GAMEPAD_BUTTONS) &&
   3263         (CORE.Input.Gamepad.previousButtonState[gamepad][button] == 0) && (CORE.Input.Gamepad.currentButtonState[gamepad][button] == 1)) pressed = true;
   3264 
   3265     return pressed;
   3266 }
   3267 
   3268 // Check if a gamepad button is being pressed
   3269 bool IsGamepadButtonDown(int gamepad, int button)
   3270 {
   3271     bool down = false;
   3272 
   3273     if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (button < MAX_GAMEPAD_BUTTONS) &&
   3274         (CORE.Input.Gamepad.currentButtonState[gamepad][button] == 1)) down = true;
   3275 
   3276     return down;
   3277 }
   3278 
   3279 // Check if a gamepad button has NOT been pressed once
   3280 bool IsGamepadButtonReleased(int gamepad, int button)
   3281 {
   3282     bool released = false;
   3283 
   3284     if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (button < MAX_GAMEPAD_BUTTONS) &&
   3285         (CORE.Input.Gamepad.previousButtonState[gamepad][button] == 1) && (CORE.Input.Gamepad.currentButtonState[gamepad][button] == 0)) released = true;
   3286 
   3287     return released;
   3288 }
   3289 
   3290 // Check if a gamepad button is NOT being pressed
   3291 bool IsGamepadButtonUp(int gamepad, int button)
   3292 {
   3293     bool up = false;
   3294 
   3295     if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (button < MAX_GAMEPAD_BUTTONS) &&
   3296         (CORE.Input.Gamepad.currentButtonState[gamepad][button] == 0)) up = true;
   3297 
   3298     return up;
   3299 }
   3300 
   3301 // Get the last gamepad button pressed
   3302 int GetGamepadButtonPressed(void)
   3303 {
   3304     return CORE.Input.Gamepad.lastButtonPressed;
   3305 }
   3306 
   3307 // Get gamepad axis count
   3308 int GetGamepadAxisCount(int gamepad)
   3309 {
   3310     return CORE.Input.Gamepad.axisCount[gamepad];
   3311 }
   3312 
   3313 // Get axis movement vector for a gamepad
   3314 float GetGamepadAxisMovement(int gamepad, int axis)
   3315 {
   3316     float value = (axis == GAMEPAD_AXIS_LEFT_TRIGGER || axis == GAMEPAD_AXIS_RIGHT_TRIGGER)? -1.0f : 0.0f;
   3317 
   3318     if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (axis < MAX_GAMEPAD_AXIS)) {
   3319         float movement = value < 0.0f ? CORE.Input.Gamepad.axisState[gamepad][axis] : fabsf(CORE.Input.Gamepad.axisState[gamepad][axis]);
   3320 
   3321         if (movement > value) value = CORE.Input.Gamepad.axisState[gamepad][axis];
   3322     }
   3323 
   3324     return value;
   3325 }
   3326 
   3327 //----------------------------------------------------------------------------------
   3328 // Module Functions Definition: Input Handling: Mouse
   3329 //----------------------------------------------------------------------------------
   3330 
   3331 // NOTE: Functions with a platform-specific implementation on rcore_<platform>.c
   3332 //void SetMousePosition(int x, int y)
   3333 //void SetMouseCursor(int cursor)
   3334 
   3335 // Check if a mouse button has been pressed once
   3336 bool IsMouseButtonPressed(int button)
   3337 {
   3338     bool pressed = false;
   3339 
   3340     if ((CORE.Input.Mouse.currentButtonState[button] == 1) && (CORE.Input.Mouse.previousButtonState[button] == 0)) pressed = true;
   3341 
   3342     // Map touches to mouse buttons checking
   3343     if ((CORE.Input.Touch.currentTouchState[button] == 1) && (CORE.Input.Touch.previousTouchState[button] == 0)) pressed = true;
   3344 
   3345     return pressed;
   3346 }
   3347 
   3348 // Check if a mouse button is being pressed
   3349 bool IsMouseButtonDown(int button)
   3350 {
   3351     bool down = false;
   3352 
   3353     if (CORE.Input.Mouse.currentButtonState[button] == 1) down = true;
   3354 
   3355     // NOTE: Touches are considered like mouse buttons
   3356     if (CORE.Input.Touch.currentTouchState[button] == 1) down = true;
   3357 
   3358     return down;
   3359 }
   3360 
   3361 // Check if a mouse button has been released once
   3362 bool IsMouseButtonReleased(int button)
   3363 {
   3364     bool released = false;
   3365 
   3366     if ((CORE.Input.Mouse.currentButtonState[button] == 0) && (CORE.Input.Mouse.previousButtonState[button] == 1)) released = true;
   3367 
   3368     // Map touches to mouse buttons checking
   3369     if ((CORE.Input.Touch.currentTouchState[button] == 0) && (CORE.Input.Touch.previousTouchState[button] == 1)) released = true;
   3370 
   3371     return released;
   3372 }
   3373 
   3374 // Check if a mouse button is NOT being pressed
   3375 bool IsMouseButtonUp(int button)
   3376 {
   3377     bool up = false;
   3378 
   3379     if (CORE.Input.Mouse.currentButtonState[button] == 0) up = true;
   3380 
   3381     // NOTE: Touches are considered like mouse buttons
   3382     if (CORE.Input.Touch.currentTouchState[button] == 0) up = true;
   3383 
   3384     return up;
   3385 }
   3386 
   3387 // Get mouse position X
   3388 int GetMouseX(void)
   3389 {
   3390     int mouseX = (int)((CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x);
   3391     return mouseX;
   3392 }
   3393 
   3394 // Get mouse position Y
   3395 int GetMouseY(void)
   3396 {
   3397     int mouseY = (int)((CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y);
   3398     return mouseY;
   3399 }
   3400 
   3401 // Get mouse position XY
   3402 Vector2 GetMousePosition(void)
   3403 {
   3404     Vector2 position = { 0 };
   3405 
   3406     position.x = (CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x;
   3407     position.y = (CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y;
   3408 
   3409     return position;
   3410 }
   3411 
   3412 // Get mouse delta between frames
   3413 Vector2 GetMouseDelta(void)
   3414 {
   3415     Vector2 delta = { 0 };
   3416 
   3417     delta.x = CORE.Input.Mouse.currentPosition.x - CORE.Input.Mouse.previousPosition.x;
   3418     delta.y = CORE.Input.Mouse.currentPosition.y - CORE.Input.Mouse.previousPosition.y;
   3419 
   3420     return delta;
   3421 }
   3422 
   3423 // Set mouse offset
   3424 // NOTE: Useful when rendering to different size targets
   3425 void SetMouseOffset(int offsetX, int offsetY)
   3426 {
   3427     CORE.Input.Mouse.offset = (Vector2){ (float)offsetX, (float)offsetY };
   3428 }
   3429 
   3430 // Set mouse scaling
   3431 // NOTE: Useful when rendering to different size targets
   3432 void SetMouseScale(float scaleX, float scaleY)
   3433 {
   3434     CORE.Input.Mouse.scale = (Vector2){ scaleX, scaleY };
   3435 }
   3436 
   3437 // Get mouse wheel movement Y
   3438 float GetMouseWheelMove(void)
   3439 {
   3440     float result = 0.0f;
   3441 
   3442     if (fabsf(CORE.Input.Mouse.currentWheelMove.x) > fabsf(CORE.Input.Mouse.currentWheelMove.y)) result = (float)CORE.Input.Mouse.currentWheelMove.x;
   3443     else result = (float)CORE.Input.Mouse.currentWheelMove.y;
   3444 
   3445     return result;
   3446 }
   3447 
   3448 // Get mouse wheel movement X/Y as a vector
   3449 Vector2 GetMouseWheelMoveV(void)
   3450 {
   3451     Vector2 result = { 0 };
   3452 
   3453     result = CORE.Input.Mouse.currentWheelMove;
   3454 
   3455     return result;
   3456 }
   3457 
   3458 //----------------------------------------------------------------------------------
   3459 // Module Functions Definition: Input Handling: Touch
   3460 //----------------------------------------------------------------------------------
   3461 
   3462 // Get touch position X for touch point 0 (relative to screen size)
   3463 int GetTouchX(void)
   3464 {
   3465     int touchX = (int)CORE.Input.Touch.position[0].x;
   3466     return touchX;
   3467 }
   3468 
   3469 // Get touch position Y for touch point 0 (relative to screen size)
   3470 int GetTouchY(void)
   3471 {
   3472     int touchY = (int)CORE.Input.Touch.position[0].y;
   3473     return touchY;
   3474 }
   3475 
   3476 // Get touch position XY for a touch point index (relative to screen size)
   3477 // TODO: Touch position should be scaled depending on display size and render size
   3478 Vector2 GetTouchPosition(int index)
   3479 {
   3480     Vector2 position = { -1.0f, -1.0f };
   3481 
   3482     if (index < MAX_TOUCH_POINTS) position = CORE.Input.Touch.position[index];
   3483     else TRACELOG(LOG_WARNING, "INPUT: Required touch point out of range (Max touch points: %i)", MAX_TOUCH_POINTS);
   3484 
   3485     return position;
   3486 }
   3487 
   3488 // Get touch point identifier for given index
   3489 int GetTouchPointId(int index)
   3490 {
   3491     int id = -1;
   3492 
   3493     if (index < MAX_TOUCH_POINTS) id = CORE.Input.Touch.pointId[index];
   3494 
   3495     return id;
   3496 }
   3497 
   3498 // Get number of touch points
   3499 int GetTouchPointCount(void)
   3500 {
   3501     return CORE.Input.Touch.pointCount;
   3502 }
   3503 
   3504 //----------------------------------------------------------------------------------
   3505 // Module Internal Functions Definition
   3506 //----------------------------------------------------------------------------------
   3507 
   3508 // NOTE: Functions with a platform-specific implementation on rcore_<platform>.c
   3509 //int InitPlatform(void)
   3510 //void ClosePlatform(void)
   3511 
   3512 // Initialize hi-resolution timer
   3513 void InitTimer(void)
   3514 {
   3515     // Setting a higher resolution can improve the accuracy of time-out intervals in wait functions
   3516     // However, it can also reduce overall system performance, because the thread scheduler switches tasks more often
   3517     // High resolutions can also prevent the CPU power management system from entering power-saving modes
   3518     // Setting a higher resolution does not improve the accuracy of the high-resolution performance counter
   3519 #if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) && !defined(PLATFORM_DESKTOP_SDL)
   3520     timeBeginPeriod(1);                 // Setup high-resolution timer to 1ms (granularity of 1-2 ms)
   3521 #endif
   3522 
   3523 #if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__EMSCRIPTEN__)
   3524     struct timespec now = { 0 };
   3525 
   3526     if (clock_gettime(CLOCK_MONOTONIC, &now) == 0)  // Success
   3527     {
   3528         CORE.Time.base = (unsigned long long int)now.tv_sec*1000000000LLU + (unsigned long long int)now.tv_nsec;
   3529     }
   3530     else TRACELOG(LOG_WARNING, "TIMER: Hi-resolution timer not available");
   3531 #endif
   3532 
   3533     CORE.Time.previous = GetTime();     // Get time as double
   3534 }
   3535 
   3536 // Set viewport for a provided width and height
   3537 void SetupViewport(int width, int height)
   3538 {
   3539     CORE.Window.render.width = width;
   3540     CORE.Window.render.height = height;
   3541 
   3542     // Set viewport width and height
   3543     // NOTE: We consider render size (scaled) and offset in case black bars are required and
   3544     // render area does not match full display area (this situation is only applicable on fullscreen mode)
   3545 #if defined(__APPLE__)
   3546     Vector2 scale = GetWindowScaleDPI();
   3547     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);
   3548 #else
   3549     rlViewport(CORE.Window.renderOffset.x/2, CORE.Window.renderOffset.y/2, CORE.Window.render.width, CORE.Window.render.height);
   3550 #endif
   3551 
   3552     rlMatrixMode(RL_PROJECTION);        // Switch to projection matrix
   3553     rlLoadIdentity();                   // Reset current matrix (projection)
   3554 
   3555     // Set orthographic projection to current framebuffer size
   3556     // NOTE: Configured top-left corner as (0, 0)
   3557     rlOrtho(0, CORE.Window.render.width, CORE.Window.render.height, 0, 0.0f, 1.0f);
   3558 
   3559     rlMatrixMode(RL_MODELVIEW);         // Switch back to modelview matrix
   3560     rlLoadIdentity();                   // Reset current matrix (modelview)
   3561 }
   3562 
   3563 // Compute framebuffer size relative to screen size and display size
   3564 // NOTE: Global variables CORE.Window.render.width/CORE.Window.render.height and CORE.Window.renderOffset.x/CORE.Window.renderOffset.y can be modified
   3565 void SetupFramebuffer(int width, int height)
   3566 {
   3567     // Calculate CORE.Window.render.width and CORE.Window.render.height, we have the display size (input params) and the desired screen size (global var)
   3568     if ((CORE.Window.screen.width > CORE.Window.display.width) || (CORE.Window.screen.height > CORE.Window.display.height))
   3569     {
   3570         TRACELOG(LOG_WARNING, "DISPLAY: Downscaling required: Screen size (%ix%i) is bigger than display size (%ix%i)", CORE.Window.screen.width, CORE.Window.screen.height, CORE.Window.display.width, CORE.Window.display.height);
   3571 
   3572         // Downscaling to fit display with border-bars
   3573         float widthRatio = (float)CORE.Window.display.width/(float)CORE.Window.screen.width;
   3574         float heightRatio = (float)CORE.Window.display.height/(float)CORE.Window.screen.height;
   3575 
   3576         if (widthRatio <= heightRatio)
   3577         {
   3578             CORE.Window.render.width = CORE.Window.display.width;
   3579             CORE.Window.render.height = (int)round((float)CORE.Window.screen.height*widthRatio);
   3580             CORE.Window.renderOffset.x = 0;
   3581             CORE.Window.renderOffset.y = (CORE.Window.display.height - CORE.Window.render.height);
   3582         }
   3583         else
   3584         {
   3585             CORE.Window.render.width = (int)round((float)CORE.Window.screen.width*heightRatio);
   3586             CORE.Window.render.height = CORE.Window.display.height;
   3587             CORE.Window.renderOffset.x = (CORE.Window.display.width - CORE.Window.render.width);
   3588             CORE.Window.renderOffset.y = 0;
   3589         }
   3590 
   3591         // Screen scaling required
   3592         float scaleRatio = (float)CORE.Window.render.width/(float)CORE.Window.screen.width;
   3593         CORE.Window.screenScale = MatrixScale(scaleRatio, scaleRatio, 1.0f);
   3594 
   3595         // NOTE: We render to full display resolution!
   3596         // We just need to calculate above parameters for downscale matrix and offsets
   3597         CORE.Window.render.width = CORE.Window.display.width;
   3598         CORE.Window.render.height = CORE.Window.display.height;
   3599 
   3600         TRACELOG(LOG_WARNING, "DISPLAY: Downscale matrix generated, content will be rendered at (%ix%i)", CORE.Window.render.width, CORE.Window.render.height);
   3601     }
   3602     else if ((CORE.Window.screen.width < CORE.Window.display.width) || (CORE.Window.screen.height < CORE.Window.display.height))
   3603     {
   3604         // Required screen size is smaller than display size
   3605         TRACELOG(LOG_INFO, "DISPLAY: Upscaling required: Screen size (%ix%i) smaller than display size (%ix%i)", CORE.Window.screen.width, CORE.Window.screen.height, CORE.Window.display.width, CORE.Window.display.height);
   3606 
   3607         if ((CORE.Window.screen.width == 0) || (CORE.Window.screen.height == 0))
   3608         {
   3609             CORE.Window.screen.width = CORE.Window.display.width;
   3610             CORE.Window.screen.height = CORE.Window.display.height;
   3611         }
   3612 
   3613         // Upscaling to fit display with border-bars
   3614         float displayRatio = (float)CORE.Window.display.width/(float)CORE.Window.display.height;
   3615         float screenRatio = (float)CORE.Window.screen.width/(float)CORE.Window.screen.height;
   3616 
   3617         if (displayRatio <= screenRatio)
   3618         {
   3619             CORE.Window.render.width = CORE.Window.screen.width;
   3620             CORE.Window.render.height = (int)round((float)CORE.Window.screen.width/displayRatio);
   3621             CORE.Window.renderOffset.x = 0;
   3622             CORE.Window.renderOffset.y = (CORE.Window.render.height - CORE.Window.screen.height);
   3623         }
   3624         else
   3625         {
   3626             CORE.Window.render.width = (int)round((float)CORE.Window.screen.height*displayRatio);
   3627             CORE.Window.render.height = CORE.Window.screen.height;
   3628             CORE.Window.renderOffset.x = (CORE.Window.render.width - CORE.Window.screen.width);
   3629             CORE.Window.renderOffset.y = 0;
   3630         }
   3631     }
   3632     else
   3633     {
   3634         CORE.Window.render.width = CORE.Window.screen.width;
   3635         CORE.Window.render.height = CORE.Window.screen.height;
   3636         CORE.Window.renderOffset.x = 0;
   3637         CORE.Window.renderOffset.y = 0;
   3638     }
   3639 }
   3640 
   3641 // Scan all files and directories in a base path
   3642 // WARNING: files.paths[] must be previously allocated and
   3643 // contain enough space to store all required paths
   3644 static void ScanDirectoryFiles(const char *basePath, FilePathList *files, const char *filter)
   3645 {
   3646     static char path[MAX_FILEPATH_LENGTH] = { 0 };
   3647     memset(path, 0, MAX_FILEPATH_LENGTH);
   3648 
   3649     struct dirent *dp = NULL;
   3650     DIR *dir = opendir(basePath);
   3651 
   3652     if (dir != NULL)
   3653     {
   3654         while ((dp = readdir(dir)) != NULL)
   3655         {
   3656             if ((strcmp(dp->d_name, ".") != 0) &&
   3657                 (strcmp(dp->d_name, "..") != 0))
   3658             {
   3659             #if defined(_WIN32)
   3660                 sprintf(path, "%s\\%s", basePath, dp->d_name);
   3661             #else
   3662                 sprintf(path, "%s/%s", basePath, dp->d_name);
   3663             #endif
   3664 
   3665                 if (filter != NULL)
   3666                 {
   3667                     if (IsPathFile(path))
   3668                     {
   3669                         if (IsFileExtension(path, filter))
   3670                         {
   3671                             strcpy(files->paths[files->count], path);
   3672                             files->count++;
   3673                         }
   3674                     }
   3675                     else
   3676                     {
   3677                         if (TextFindIndex(filter, DIRECTORY_FILTER_TAG) >= 0)
   3678                         {
   3679                             strcpy(files->paths[files->count], path);
   3680                             files->count++;
   3681                         }
   3682                     }
   3683                 }
   3684                 else
   3685                 {
   3686                     strcpy(files->paths[files->count], path);
   3687                     files->count++;
   3688                 }
   3689             }
   3690         }
   3691 
   3692         closedir(dir);
   3693     }
   3694     else TRACELOG(LOG_WARNING, "FILEIO: Directory cannot be opened (%s)", basePath);
   3695 }
   3696 
   3697 // Scan all files and directories recursively from a base path
   3698 static void ScanDirectoryFilesRecursively(const char *basePath, FilePathList *files, const char *filter)
   3699 {
   3700     char path[MAX_FILEPATH_LENGTH] = { 0 };
   3701     memset(path, 0, MAX_FILEPATH_LENGTH);
   3702 
   3703     struct dirent *dp = NULL;
   3704     DIR *dir = opendir(basePath);
   3705 
   3706     if (dir != NULL)
   3707     {
   3708         while (((dp = readdir(dir)) != NULL) && (files->count < files->capacity))
   3709         {
   3710             if ((strcmp(dp->d_name, ".") != 0) && (strcmp(dp->d_name, "..") != 0))
   3711             {
   3712                 // Construct new path from our base path
   3713             #if defined(_WIN32)
   3714                 sprintf(path, "%s\\%s", basePath, dp->d_name);
   3715             #else
   3716                 sprintf(path, "%s/%s", basePath, dp->d_name);
   3717             #endif
   3718 
   3719                 if (IsPathFile(path))
   3720                 {
   3721                     if (filter != NULL)
   3722                     {
   3723                         if (IsFileExtension(path, filter))
   3724                         {
   3725                             strcpy(files->paths[files->count], path);
   3726                             files->count++;
   3727                         }
   3728                     }
   3729                     else
   3730                     {
   3731                         strcpy(files->paths[files->count], path);
   3732                         files->count++;
   3733                     }
   3734 
   3735                     if (files->count >= files->capacity)
   3736                     {
   3737                         TRACELOG(LOG_WARNING, "FILEIO: Maximum filepath scan capacity reached (%i files)", files->capacity);
   3738                         break;
   3739                     }
   3740                 }
   3741                 else
   3742                 {
   3743                     if ((filter != NULL) && (TextFindIndex(filter, DIRECTORY_FILTER_TAG) >= 0))
   3744                     {
   3745                         strcpy(files->paths[files->count], path);
   3746                         files->count++;
   3747                     }
   3748 
   3749                     if (files->count >= files->capacity)
   3750                     {
   3751                         TRACELOG(LOG_WARNING, "FILEIO: Maximum filepath scan capacity reached (%i files)", files->capacity);
   3752                         break;
   3753                     }
   3754 
   3755                     ScanDirectoryFilesRecursively(path, files, filter);
   3756                 }
   3757             }
   3758         }
   3759 
   3760         closedir(dir);
   3761     }
   3762     else TRACELOG(LOG_WARNING, "FILEIO: Directory cannot be opened (%s)", basePath);
   3763 }
   3764 
   3765 #if defined(SUPPORT_AUTOMATION_EVENTS)
   3766 // Automation event recording
   3767 // NOTE: Recording is by default done at EndDrawing(), before PollInputEvents()
   3768 static void RecordAutomationEvent(void)
   3769 {
   3770     // Checking events in current frame and save them into currentEventList
   3771     // TODO: How important is the current frame? Could it be modified?
   3772 
   3773     if (currentEventList->count == currentEventList->capacity) return;    // Security check
   3774 
   3775     // Keyboard input events recording
   3776     //-------------------------------------------------------------------------------------
   3777     for (int key = 0; key < MAX_KEYBOARD_KEYS; key++)
   3778     {
   3779         // Event type: INPUT_KEY_UP (only saved once)
   3780         if (CORE.Input.Keyboard.previousKeyState[key] && !CORE.Input.Keyboard.currentKeyState[key])
   3781         {
   3782             currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
   3783             currentEventList->events[currentEventList->count].type = INPUT_KEY_UP;
   3784             currentEventList->events[currentEventList->count].params[0] = key;
   3785             currentEventList->events[currentEventList->count].params[1] = 0;
   3786             currentEventList->events[currentEventList->count].params[2] = 0;
   3787 
   3788             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]);
   3789             currentEventList->count++;
   3790         }
   3791 
   3792         if (currentEventList->count == currentEventList->capacity) return;    // Security check
   3793 
   3794         // Event type: INPUT_KEY_DOWN
   3795         if (CORE.Input.Keyboard.currentKeyState[key])
   3796         {
   3797             currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
   3798             currentEventList->events[currentEventList->count].type = INPUT_KEY_DOWN;
   3799             currentEventList->events[currentEventList->count].params[0] = key;
   3800             currentEventList->events[currentEventList->count].params[1] = 0;
   3801             currentEventList->events[currentEventList->count].params[2] = 0;
   3802 
   3803             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]);
   3804             currentEventList->count++;
   3805         }
   3806 
   3807         if (currentEventList->count == currentEventList->capacity) return;    // Security check
   3808     }
   3809     //-------------------------------------------------------------------------------------
   3810 
   3811     // Mouse input currentEventList->events recording
   3812     //-------------------------------------------------------------------------------------
   3813     for (int button = 0; button < MAX_MOUSE_BUTTONS; button++)
   3814     {
   3815         // Event type: INPUT_MOUSE_BUTTON_UP
   3816         if (CORE.Input.Mouse.previousButtonState[button] && !CORE.Input.Mouse.currentButtonState[button])
   3817         {
   3818             currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
   3819             currentEventList->events[currentEventList->count].type = INPUT_MOUSE_BUTTON_UP;
   3820             currentEventList->events[currentEventList->count].params[0] = button;
   3821             currentEventList->events[currentEventList->count].params[1] = 0;
   3822             currentEventList->events[currentEventList->count].params[2] = 0;
   3823 
   3824             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]);
   3825             currentEventList->count++;
   3826         }
   3827 
   3828         if (currentEventList->count == currentEventList->capacity) return;    // Security check
   3829 
   3830         // Event type: INPUT_MOUSE_BUTTON_DOWN
   3831         if (CORE.Input.Mouse.currentButtonState[button])
   3832         {
   3833             currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
   3834             currentEventList->events[currentEventList->count].type = INPUT_MOUSE_BUTTON_DOWN;
   3835             currentEventList->events[currentEventList->count].params[0] = button;
   3836             currentEventList->events[currentEventList->count].params[1] = 0;
   3837             currentEventList->events[currentEventList->count].params[2] = 0;
   3838 
   3839             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]);
   3840             currentEventList->count++;
   3841         }
   3842 
   3843         if (currentEventList->count == currentEventList->capacity) return;    // Security check
   3844     }
   3845 
   3846     // Event type: INPUT_MOUSE_POSITION (only saved if changed)
   3847     if (((int)CORE.Input.Mouse.currentPosition.x != (int)CORE.Input.Mouse.previousPosition.x) ||
   3848         ((int)CORE.Input.Mouse.currentPosition.y != (int)CORE.Input.Mouse.previousPosition.y))
   3849     {
   3850         currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
   3851         currentEventList->events[currentEventList->count].type = INPUT_MOUSE_POSITION;
   3852         currentEventList->events[currentEventList->count].params[0] = (int)CORE.Input.Mouse.currentPosition.x;
   3853         currentEventList->events[currentEventList->count].params[1] = (int)CORE.Input.Mouse.currentPosition.y;
   3854         currentEventList->events[currentEventList->count].params[2] = 0;
   3855 
   3856         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]);
   3857         currentEventList->count++;
   3858 
   3859         if (currentEventList->count == currentEventList->capacity) return;    // Security check
   3860     }
   3861 
   3862     // Event type: INPUT_MOUSE_WHEEL_MOTION
   3863     if (((int)CORE.Input.Mouse.currentWheelMove.x != (int)CORE.Input.Mouse.previousWheelMove.x) ||
   3864         ((int)CORE.Input.Mouse.currentWheelMove.y != (int)CORE.Input.Mouse.previousWheelMove.y))
   3865     {
   3866         currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
   3867         currentEventList->events[currentEventList->count].type = INPUT_MOUSE_WHEEL_MOTION;
   3868         currentEventList->events[currentEventList->count].params[0] = (int)CORE.Input.Mouse.currentWheelMove.x;
   3869         currentEventList->events[currentEventList->count].params[1] = (int)CORE.Input.Mouse.currentWheelMove.y;
   3870         currentEventList->events[currentEventList->count].params[2] = 0;
   3871 
   3872         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]);
   3873         currentEventList->count++;
   3874 
   3875         if (currentEventList->count == currentEventList->capacity) return;    // Security check
   3876     }
   3877     //-------------------------------------------------------------------------------------
   3878 
   3879     // Touch input currentEventList->events recording
   3880     //-------------------------------------------------------------------------------------
   3881     for (int id = 0; id < MAX_TOUCH_POINTS; id++)
   3882     {
   3883         // Event type: INPUT_TOUCH_UP
   3884         if (CORE.Input.Touch.previousTouchState[id] && !CORE.Input.Touch.currentTouchState[id])
   3885         {
   3886             currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
   3887             currentEventList->events[currentEventList->count].type = INPUT_TOUCH_UP;
   3888             currentEventList->events[currentEventList->count].params[0] = id;
   3889             currentEventList->events[currentEventList->count].params[1] = 0;
   3890             currentEventList->events[currentEventList->count].params[2] = 0;
   3891 
   3892             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]);
   3893             currentEventList->count++;
   3894         }
   3895 
   3896         if (currentEventList->count == currentEventList->capacity) return;    // Security check
   3897 
   3898         // Event type: INPUT_TOUCH_DOWN
   3899         if (CORE.Input.Touch.currentTouchState[id])
   3900         {
   3901             currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
   3902             currentEventList->events[currentEventList->count].type = INPUT_TOUCH_DOWN;
   3903             currentEventList->events[currentEventList->count].params[0] = id;
   3904             currentEventList->events[currentEventList->count].params[1] = 0;
   3905             currentEventList->events[currentEventList->count].params[2] = 0;
   3906 
   3907             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]);
   3908             currentEventList->count++;
   3909         }
   3910 
   3911         if (currentEventList->count == currentEventList->capacity) return;    // Security check
   3912 
   3913         // Event type: INPUT_TOUCH_POSITION
   3914         // TODO: It requires the id!
   3915         /*
   3916         if (((int)CORE.Input.Touch.currentPosition[id].x != (int)CORE.Input.Touch.previousPosition[id].x) ||
   3917             ((int)CORE.Input.Touch.currentPosition[id].y != (int)CORE.Input.Touch.previousPosition[id].y))
   3918         {
   3919             currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
   3920             currentEventList->events[currentEventList->count].type = INPUT_TOUCH_POSITION;
   3921             currentEventList->events[currentEventList->count].params[0] = id;
   3922             currentEventList->events[currentEventList->count].params[1] = (int)CORE.Input.Touch.currentPosition[id].x;
   3923             currentEventList->events[currentEventList->count].params[2] = (int)CORE.Input.Touch.currentPosition[id].y;
   3924 
   3925             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]);
   3926             currentEventList->count++;
   3927         }
   3928         */
   3929 
   3930         if (currentEventList->count == currentEventList->capacity) return;    // Security check
   3931     }
   3932     //-------------------------------------------------------------------------------------
   3933 
   3934     // Gamepad input currentEventList->events recording
   3935     //-------------------------------------------------------------------------------------
   3936     for (int gamepad = 0; gamepad < MAX_GAMEPADS; gamepad++)
   3937     {
   3938         // Event type: INPUT_GAMEPAD_CONNECT
   3939         /*
   3940         if ((CORE.Input.Gamepad.currentState[gamepad] != CORE.Input.Gamepad.previousState[gamepad]) &&
   3941             (CORE.Input.Gamepad.currentState[gamepad])) // Check if changed to ready
   3942         {
   3943             // TODO: Save gamepad connect event
   3944         }
   3945         */
   3946 
   3947         // Event type: INPUT_GAMEPAD_DISCONNECT
   3948         /*
   3949         if ((CORE.Input.Gamepad.currentState[gamepad] != CORE.Input.Gamepad.previousState[gamepad]) &&
   3950             (!CORE.Input.Gamepad.currentState[gamepad])) // Check if changed to not-ready
   3951         {
   3952             // TODO: Save gamepad disconnect event
   3953         }
   3954         */
   3955 
   3956         for (int button = 0; button < MAX_GAMEPAD_BUTTONS; button++)
   3957         {
   3958             // Event type: INPUT_GAMEPAD_BUTTON_UP
   3959             if (CORE.Input.Gamepad.previousButtonState[gamepad][button] && !CORE.Input.Gamepad.currentButtonState[gamepad][button])
   3960             {
   3961                 currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
   3962                 currentEventList->events[currentEventList->count].type = INPUT_GAMEPAD_BUTTON_UP;
   3963                 currentEventList->events[currentEventList->count].params[0] = gamepad;
   3964                 currentEventList->events[currentEventList->count].params[1] = button;
   3965                 currentEventList->events[currentEventList->count].params[2] = 0;
   3966 
   3967                 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]);
   3968                 currentEventList->count++;
   3969             }
   3970 
   3971             if (currentEventList->count == currentEventList->capacity) return;    // Security check
   3972 
   3973             // Event type: INPUT_GAMEPAD_BUTTON_DOWN
   3974             if (CORE.Input.Gamepad.currentButtonState[gamepad][button])
   3975             {
   3976                 currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
   3977                 currentEventList->events[currentEventList->count].type = INPUT_GAMEPAD_BUTTON_DOWN;
   3978                 currentEventList->events[currentEventList->count].params[0] = gamepad;
   3979                 currentEventList->events[currentEventList->count].params[1] = button;
   3980                 currentEventList->events[currentEventList->count].params[2] = 0;
   3981 
   3982                 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]);
   3983                 currentEventList->count++;
   3984             }
   3985 
   3986             if (currentEventList->count == currentEventList->capacity) return;    // Security check
   3987         }
   3988 
   3989         for (int axis = 0; axis < MAX_GAMEPAD_AXIS; axis++)
   3990         {
   3991             // Event type: INPUT_GAMEPAD_AXIS_MOTION
   3992             float defaultMovement = (axis == GAMEPAD_AXIS_LEFT_TRIGGER || axis == GAMEPAD_AXIS_RIGHT_TRIGGER)? -1.0f : 0.0f;
   3993             if (GetGamepadAxisMovement(gamepad, axis) != defaultMovement)
   3994             {
   3995                 currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
   3996                 currentEventList->events[currentEventList->count].type = INPUT_GAMEPAD_AXIS_MOTION;
   3997                 currentEventList->events[currentEventList->count].params[0] = gamepad;
   3998                 currentEventList->events[currentEventList->count].params[1] = axis;
   3999                 currentEventList->events[currentEventList->count].params[2] = (int)(CORE.Input.Gamepad.axisState[gamepad][axis]*32768.0f);
   4000 
   4001                 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]);
   4002                 currentEventList->count++;
   4003             }
   4004 
   4005             if (currentEventList->count == currentEventList->capacity) return;    // Security check
   4006         }
   4007     }
   4008     //-------------------------------------------------------------------------------------
   4009 
   4010 #if defined(SUPPORT_GESTURES_SYSTEM)
   4011     // Gestures input currentEventList->events recording
   4012     //-------------------------------------------------------------------------------------
   4013     if (GESTURES.current != GESTURE_NONE)
   4014     {
   4015         // Event type: INPUT_GESTURE
   4016         currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
   4017         currentEventList->events[currentEventList->count].type = INPUT_GESTURE;
   4018         currentEventList->events[currentEventList->count].params[0] = GESTURES.current;
   4019         currentEventList->events[currentEventList->count].params[1] = 0;
   4020         currentEventList->events[currentEventList->count].params[2] = 0;
   4021 
   4022         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]);
   4023         currentEventList->count++;
   4024 
   4025         if (currentEventList->count == currentEventList->capacity) return;    // Security check
   4026     }
   4027     //-------------------------------------------------------------------------------------
   4028 #endif
   4029 }
   4030 #endif
   4031 
   4032 #if !defined(SUPPORT_MODULE_RTEXT)
   4033 // Formatting of text with variables to 'embed'
   4034 // WARNING: String returned will expire after this function is called MAX_TEXTFORMAT_BUFFERS times
   4035 const char *TextFormat(const char *text, ...)
   4036 {
   4037 #ifndef MAX_TEXTFORMAT_BUFFERS
   4038     #define MAX_TEXTFORMAT_BUFFERS      4        // Maximum number of static buffers for text formatting
   4039 #endif
   4040 #ifndef MAX_TEXT_BUFFER_LENGTH
   4041     #define MAX_TEXT_BUFFER_LENGTH   1024        // Maximum size of static text buffer
   4042 #endif
   4043 
   4044     // We create an array of buffers so strings don't expire until MAX_TEXTFORMAT_BUFFERS invocations
   4045     static char buffers[MAX_TEXTFORMAT_BUFFERS][MAX_TEXT_BUFFER_LENGTH] = { 0 };
   4046     static int index = 0;
   4047 
   4048     char *currentBuffer = buffers[index];
   4049     memset(currentBuffer, 0, MAX_TEXT_BUFFER_LENGTH);   // Clear buffer before using
   4050 
   4051     va_list args;
   4052     va_start(args, text);
   4053     int requiredByteCount = vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args);
   4054     va_end(args);
   4055 
   4056     // If requiredByteCount is larger than the MAX_TEXT_BUFFER_LENGTH, then overflow occured
   4057     if (requiredByteCount >= MAX_TEXT_BUFFER_LENGTH)
   4058     {
   4059         // Inserting "..." at the end of the string to mark as truncated
   4060         char *truncBuffer = buffers[index] + MAX_TEXT_BUFFER_LENGTH - 4; // Adding 4 bytes = "...\0"
   4061         sprintf(truncBuffer, "...");
   4062     }
   4063 
   4064     index += 1;     // Move to next buffer for next function call
   4065     if (index >= MAX_TEXTFORMAT_BUFFERS) index = 0;
   4066 
   4067     return currentBuffer;
   4068 }
   4069 
   4070 #endif // !SUPPORT_MODULE_RTEXT