Logo

index : raylib-jai

---

  • summary
  • about
  • tree
  • log
  • branches
<< path: root/public/raylib-jai.git/html/Raylib/raylib/src/rcore.c blob: ea38300f17ee6c87532b04ed96ced4ba53b80895 [raw] [clear marker]

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