Merge branch 'raysan5:master' into master

This commit is contained in:
Le Juez Victor 2026-04-04 14:08:30 +02:00 committed by GitHub
commit a1de46c384
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
90 changed files with 9924 additions and 1381 deletions

1
.gitignore vendored
View File

@ -118,6 +118,7 @@ GTAGS
# Zig programming language # Zig programming language
.zig-cache/ .zig-cache/
zig-cache/ zig-cache/
zig-pkg/
zig-out/ zig-out/
build/ build/
build-*/ build-*/

View File

@ -21,11 +21,17 @@ KEY CHANGES:
Detailed changes: Detailed changes:
[rcore] ADDED: `FileRename()`, by @raysan5
[rcore] ADDED: `FileRemove()`, by @raysan5
[rcore] ADDED: `FileCopy()`, by @raysan5
[rcore] ADDED: `FileMove()`, by @raysan5
[rcore] ADDED: `FileTextReplace()`, by @raysan5
[rcore] ADDED: `FileTextFindIndex()`, by @raysan5
[rcore] ADDED: `ComputeSHA256()` (#5264) by @iamahuman1395
[rcore] ADDED: `GetKeyName()` (#4544) by @lecongsebastien
[rcore] ADDED: Logging and file-system functionality from `utils` (#4551) by @raysan5 -WARNING- [rcore] ADDED: Logging and file-system functionality from `utils` (#4551) by @raysan5 -WARNING-
[rcore] ADDED: Flags set/clear macros: FLAG_SET, FLAG_CLEAR, FLAG_IS_SET (#5169) by @raysan5 [rcore] ADDED: Flags set/clear macros: FLAG_SET, FLAG_CLEAR, FLAG_IS_SET (#5169) by @raysan5
[rcore] ADDED: Warnings in case of no platform backend defined by @raysan5 [rcore] ADDED: Warnings in case of no platform backend defined by @raysan5
[rcore] ADDED: `ComputeSHA256()` (#5264) by @iamahuman1395
[rcore] ADDED: `GetKeyName()` (#4544) by @lecongsebastien
[rcore] REMOVED: GIF recording option, added example by @raysan5 -WARNING- [rcore] REMOVED: GIF recording option, added example by @raysan5 -WARNING-
[rcore] REMOVED: `CORE.Window.fullscreen` variable, using available flag instead by @raysan5 [rcore] REMOVED: `CORE.Window.fullscreen` variable, using available flag instead by @raysan5
[rcore] REMOVED: `SetupFramebuffer()`, most platforms do not need it any more by @raysan5 [rcore] REMOVED: `SetupFramebuffer()`, most platforms do not need it any more by @raysan5
@ -111,7 +117,7 @@ Detailed changes:
[rcore][RGFW] ADDED: New backend option: `PLATFORM_WEB_RGFW` and update RGFW (#4480) by @colleagueRiley [rcore][RGFW] ADDED: New backend option: `PLATFORM_WEB_RGFW` and update RGFW (#4480) by @colleagueRiley
[rcore][RGFW] REVIEWED: Added missing Right Control key by @M374LX [rcore][RGFW] REVIEWED: Added missing Right Control key by @M374LX
[rcore][RGFW] REVIEWED: Changed `RGFW_window_eventWait` timeout to -1 by @doggymangc [rcore][RGFW] REVIEWED: Changed `RGFW_window_eventWait` timeout to -1 by @doggymangc
[rcore][RGFW] REVIEWED: Duplicate entries reemoved from `keyMappingRGFW` (#5242) by @iamahuman1395 [rcore][RGFW] REVIEWED: Duplicate entries removed from `keyMappingRGFW` (#5242) by @iamahuman1395
[rcore][RGFW] REVIEWED: Fix Escape always closing the window by @M374LX [rcore][RGFW] REVIEWED: Fix Escape always closing the window by @M374LX
[rcore][RGFW] REVIEWED: Forward declare the windows stuff, prevents failures in GCC (#5269) by @Jeffm2501 [rcore][RGFW] REVIEWED: Forward declare the windows stuff, prevents failures in GCC (#5269) by @Jeffm2501
[rcore][RGFW] REVIEWED: Requires RGBA8 images as window icons (#5431) by @crisserpl2 [rcore][RGFW] REVIEWED: Requires RGBA8 images as window icons (#5431) by @crisserpl2
@ -198,7 +204,7 @@ Detailed changes:
[rlgl] REVIEWED: `rlActiveDrawBuffers`, fix for OpenGL ES 3.0 (#4605) by @Bigfoot71 [rlgl] REVIEWED: `rlActiveDrawBuffers`, fix for OpenGL ES 3.0 (#4605) by @Bigfoot71
[rlgl] REVIEWED: `rlGetPixelDataSize()`, correct compressed data size calculation per blocks #5416 by @raysan5 [rlgl] REVIEWED: `rlGetPixelDataSize()`, correct compressed data size calculation per blocks #5416 by @raysan5
[rlgl] REVIEWED: `instranceTransform` shader location index #4538 (#4579) by @meadiode [rlgl] REVIEWED: `instranceTransform` shader location index #4538 (#4579) by @meadiode
[rlgl] REVIEWED: `SetShaderValueTexture()`, updatee comments (#4703) by @pejorativefox [rlgl] REVIEWED: `SetShaderValueTexture()`, update comments (#4703) by @pejorativefox
[rlgl] REDESIGNED: Avoid program crash if GPU data is tried to be loaded before `InitWindow()` #4751 by @raysan5 -WARNING- [rlgl] REDESIGNED: Avoid program crash if GPU data is tried to be loaded before `InitWindow()` #4751 by @raysan5 -WARNING-
[rlgl] REDESIGNED: Shader loading API function names for more consistency #5631 by @raysan5 -WARNING- [rlgl] REDESIGNED: Shader loading API function names for more consistency #5631 by @raysan5 -WARNING-
[rlsw] ADDED: `swGetColorBuffer()` for convenience by @raysan5 [rlsw] ADDED: `swGetColorBuffer()` for convenience by @raysan5
@ -239,6 +245,13 @@ Detailed changes:
[rtextures] REVIEWED: `ImageDrawLineEx(), to be able to draw even numbered thicknesses (#5042) by @Sir-Irk [rtextures] REVIEWED: `ImageDrawLineEx(), to be able to draw even numbered thicknesses (#5042) by @Sir-Irk
[rtextures] REVIEWED: `ImageBlurGaussian()`, fix integer overflow in cast (#5037) by @garrisonhh [rtextures] REVIEWED: `ImageBlurGaussian()`, fix integer overflow in cast (#5037) by @garrisonhh
[rtext] ADDED: `LoadTextLines()`/`UnloadTextLines()` by @raysan5 [rtext] ADDED: `LoadTextLines()`/`UnloadTextLines()` by @raysan5
[rtext] ADDED: `TextInsertAlloc()`, returns memory allocated string by @raysan5
[rtext] ADDED: `TextReplace()`, using static buffer by @raysan5
[rtext] ADDED: `TextReplaceAlloc()`, returns memory allocated string by @raysan5
[rtext] ADDED: `TextReplaceBetween()`, using static buffer by @raysan5
[rtext] ADDED: `TextReplaceBetweenAlloc()`, returns memory allocated string by @raysan5
[rtext] ADDED: `GetTextBetween()`, using static buffer by @raysan5
[rtext] ADDED: `TextRemoveSpaces()`, using static buffer by @raysan5
[rtext] ADDED: `MeasureTextCodepoints()` for direct measurement of codepoints (#5623) by @creeperblin [rtext] ADDED: `MeasureTextCodepoints()` for direct measurement of codepoints (#5623) by @creeperblin
[rtext] RENAMED: Variable names for consistency, `textLength` (length in bytes) vs `textSize` (measure in pixels) by @raysan5 [rtext] RENAMED: Variable names for consistency, `textLength` (length in bytes) vs `textSize` (measure in pixels) by @raysan5
[rtext] REVIEWED: Support default font loading with no GPU enabled, to be used with Image API [rtext] REVIEWED: Support default font loading with no GPU enabled, to be used with Image API
@ -263,11 +276,10 @@ Detailed changes:
[rtext] REVIEWED: `GetCodepointCount()`, misuse of cast (#4741) by @sleeptightAnsiC [rtext] REVIEWED: `GetCodepointCount()`, misuse of cast (#4741) by @sleeptightAnsiC
[rtext] REVIEWED: `GenImageFontAtlas()`, memory corruption and invalid size calculation (#5602) by @konakona418 [rtext] REVIEWED: `GenImageFontAtlas()`, memory corruption and invalid size calculation (#5602) by @konakona418
[rtext] REVIEWED: `TextInsert()`, fix bug on insertion (#5644) by @CrackedPixel [rtext] REVIEWED: `TextInsert()`, fix bug on insertion (#5644) by @CrackedPixel
[rtext] REVIEWED: `TextInsert()`, use static buffer, easier to use by @raysan5 -WARNING-
[rtext] REVIEWED: `TextJoin()`, convert `const char **` to `char**` by @raysan5 [rtext] REVIEWED: `TextJoin()`, convert `const char **` to `char**` by @raysan5
[rtext] REVIEWED: `TextReplace()` and `TextLength()`, avoid using `strcpy()` by @raysan5
[rtext] REVIEWED: `TextReplace()` by @raysan5
[rtext] REVIEWED: `TextReplace()`, improvements (#5511) by @belan2470 [rtext] REVIEWED: `TextReplace()`, improvements (#5511) by @belan2470
[rtext] REVIEWED: `TextReplaceBetween()` by @raysan5 [rtext] REVIEWED: `TextReplace()` and `TextLength()`, avoid using `strcpy()` by @raysan5
[rtext] REVIEWED: `TextSubtext(), fixes (#4759) by @veins1 [rtext] REVIEWED: `TextSubtext(), fixes (#4759) by @veins1
[rtext] REVIEWED: `TextToPascal()`, fix issue by @raysan5 [rtext] REVIEWED: `TextToPascal()`, fix issue by @raysan5
[rtext] REVIEWED: `TextToFloat()`, remove removed inaccurate comment (#4596) by @hexmaster111 [rtext] REVIEWED: `TextToFloat()`, remove removed inaccurate comment (#4596) by @hexmaster111
@ -735,7 +747,7 @@ Detailed changes:
[rexm] REVIEWED: `ScanExampleResources()` avoid resources to be saved by the program by @raysan5 [rexm] REVIEWED: `ScanExampleResources()` avoid resources to be saved by the program by @raysan5
[rexm] REVIEWED: `UpdateSourceMetadata()` and `TextReplaceBetween()` by @raysan5 [rexm] REVIEWED: `UpdateSourceMetadata()` and `TextReplaceBetween()` by @raysan5
[rexm] REVIEWED: `examples.js` example addition working by @raysan5 [rexm] REVIEWED: `examples.js` example addition working by @raysan5
[rexm] REVIEWED: `add` command logic for existing examplee addition by @raysan5 [rexm] REVIEWED: `add` command logic for existing example addition by @raysan5
[rexm] REVIEWED: Replace example name on project file by @raysan5 [rexm] REVIEWED: Replace example name on project file by @raysan5
[rexm] REVIEWED: Report issues if logs can not be loaded by @raysan5 [rexm] REVIEWED: Report issues if logs can not be loaded by @raysan5
[rexm] REVIEWED: VS project adding to solution by @raysan5 [rexm] REVIEWED: VS project adding to solution by @raysan5
@ -747,6 +759,7 @@ Detailed changes:
[rexm] REVIEWED: `Makefile.Web` before trying to rebuild new example for web by @raysan5 [rexm] REVIEWED: `Makefile.Web` before trying to rebuild new example for web by @raysan5
[rexm] REVIEWED: Using `Makefile.Web` for specific web versions generation by @raysan5 [rexm] REVIEWED: Using `Makefile.Web` for specific web versions generation by @raysan5
[external] ADDED: `cgltf_write` 1.15, to support glTF models export in the future, by @raysan5
[external] RENAMED: `rl_gputex.h` to `rltexgpu.h`, compressed textures loading [external] RENAMED: `rl_gputex.h` to `rltexgpu.h`, compressed textures loading
[external] REVIEWED: `rltexgpu.h`, make it usable standalone by @raysan5 [external] REVIEWED: `rltexgpu.h`, make it usable standalone by @raysan5
[external] REVIEWED: `rltexgpu.h, fix the swizzling in `rl_load_dds_from_memory()` (#5422) by @msmith-codes [external] REVIEWED: `rltexgpu.h, fix the swizzling in `rl_load_dds_from_memory()` (#5422) by @msmith-codes
@ -755,16 +768,23 @@ Detailed changes:
[external] REVIEWED: `sdefl` and `sinfl` issues (#5367) by @raysan5 [external] REVIEWED: `sdefl` and `sinfl` issues (#5367) by @raysan5
[external] REVIEWED: `sinfl_bsr()`, improvements by @RicoP [external] REVIEWED: `sinfl_bsr()`, improvements by @RicoP
[external] REVIEWED: `stb_truetype`, fix composite glyph scaling logic (#4811) by @ashishbhattarai [external] REVIEWED: `stb_truetype`, fix composite glyph scaling logic (#4811) by @ashishbhattarai
[external] UPDATED: `raygui` to 5.0-dev for examples by @raysan5
[external] UPDATED: dr_libs (#5020) by @Emil2010 [external] UPDATED: dr_libs (#5020) by @Emil2010
[external] UPDATED: miniaudio to v0.11.22 (#4983) by @M374LX [external] UPDATED: `miniaudio` to v0.11.22 (#4983) by @M374LX
[external] UPDATED: miniaudio to v0.11.23 (#5234) by @pyrokn8 [external] UPDATED: `miniaudio` to v0.11.23 (#5234) by @pyrokn8
[external] UPDATED: miniaudio to v0.11.24 (#5506) by @vdemcak [external] UPDATED: `miniaudio` to v0.11.24 (#5506) by @vdemcak
[external] UPDATED: raygui to 5.0-dev for examples by @raysan5 [external] UPDATED: `RGFW` to 1.5 (#4688) by @ColleagueRiley
[external] UPDATED: RGFW to 1.5 (#4688) by @ColleagueRiley [external] UPDATED: `RGFW` to 1.6 (#4795) by @colleagueRiley
[external] UPDATED: RGFW to 1.6 (#4795) by @colleagueRiley [external] UPDATED: `RGFW` to 1.7 (#4965) by @M374LX
[external] UPDATED: RGFW to 1.7 (#4965) by @M374LX [external] UPDATED: `RGFW` to 1.7.5-dev (#4976) by @M374LX
[external] UPDATED: RGFW to 1.7.5-dev (#4976) by @M374LX [external] UPDATED: `RGFW` to 2.0.0 (#5582) by @CrackedPixel
[external] UPDATED: RGFW to 2.0.0 (#5582) by @CrackedPixel [external] UPDATED: `cgltf` 1.14 to 1.15 by @raysan5
[external] UPDATED: `dr_flac` v0.13.0 to v0.13.3 by @raysan5
[external] UPDATED: `dr_mp3` v0.7.0 to v0.7.4 by @raysan5
[external] UPDATED: `m3d` to latest master by @raysan5
[external] UPDATED: `qoi` to latest master by @raysan5
[external] UPDATED: `qoa` to latest master by @raysan5
[external] UPDATED: `stb_image_resize2` v2.12 to v2.18 by @raysan5
[misc] ADDED: SECURITY.md for security reporting policies by @raysan5 [misc] ADDED: SECURITY.md for security reporting policies by @raysan5
[misc] ADDED: `examples/examples_list`, to be used by `rexm` or other tools by @raysan5 [misc] ADDED: `examples/examples_list`, to be used by `rexm` or other tools by @raysan5

View File

@ -40,6 +40,7 @@ features
- Written in plain C code (C99) using PascalCase/camelCase notation - Written in plain C code (C99) using PascalCase/camelCase notation
- Hardware accelerated with OpenGL: **1.1, 2.1, 3.3, 4.3, ES 2.0, ES 3.0** - Hardware accelerated with OpenGL: **1.1, 2.1, 3.3, 4.3, ES 2.0, ES 3.0**
- **Unique OpenGL abstraction layer** (usable as standalone module): [rlgl](https://github.com/raysan5/raylib/blob/master/src/rlgl.h) - **Unique OpenGL abstraction layer** (usable as standalone module): [rlgl](https://github.com/raysan5/raylib/blob/master/src/rlgl.h)
- **Software Renderer** backend (no OpenGL required!): [rlsw](https://github.com/raysan5/raylib/blob/master/src/external/rlsw.h)
- Multiple **Fonts** formats supported (TTF, OTF, FNT, BDF, sprite fonts) - Multiple **Fonts** formats supported (TTF, OTF, FNT, BDF, sprite fonts)
- Multiple texture formats supported, including **compressed formats** (DXT, ETC, ASTC) - Multiple texture formats supported, including **compressed formats** (DXT, ETC, ASTC)
- **Full 3D support**, including 3D Shapes, Models, Billboards, Heightmaps and more! - **Full 3D support**, including 3D Shapes, Models, Billboards, Heightmaps and more!
@ -61,7 +62,7 @@ This is a basic raylib example, it creates a window and draws the text `"Congrat
int main(void) int main(void)
{ {
InitWindow(800, 450, "raylib [core] example - basic window"); InitWindow(800, 450, "raylib example - basic window");
while (!WindowShouldClose()) while (!WindowShouldClose())
{ {

View File

@ -6,6 +6,7 @@ Here is a wishlist with features and ideas to improve the library. Note that fea
- [GitHub PRs](https://github.com/raysan5/raylib/pulls) open with improvements to be reviewed. - [GitHub PRs](https://github.com/raysan5/raylib/pulls) open with improvements to be reviewed.
- [raylib source code](https://github.com/raysan5/raylib/tree/master/src) has multiple *TODO* comments around code with pending things to review or improve. - [raylib source code](https://github.com/raysan5/raylib/tree/master/src) has multiple *TODO* comments around code with pending things to review or improve.
- raylib wishlists discussions are open to everyone to ask for improvements, feel free to check and comment: - raylib wishlists discussions are open to everyone to ask for improvements, feel free to check and comment:
- [raylib 7.0 wishlist](https://github.com/raysan5/raylib/discussions/5710)
- [raylib 6.0 wishlist](https://github.com/raysan5/raylib/discussions/4660) - [raylib 6.0 wishlist](https://github.com/raysan5/raylib/discussions/4660)
- [raylib 5.0 wishlist](https://github.com/raysan5/raylib/discussions/2952) - [raylib 5.0 wishlist](https://github.com/raysan5/raylib/discussions/2952)
- [raylib wishlist 2022](https://github.com/raysan5/raylib/discussions/2272) - [raylib wishlist 2022](https://github.com/raysan5/raylib/discussions/2272)
@ -21,7 +22,7 @@ _Current version of raylib is complete and functional but there is always room f
- [ ] `rcore_desktop_wayland`: Create additional platform backend: Linux/Wayland - [ ] `rcore_desktop_wayland`: Create additional platform backend: Linux/Wayland
- [ ] `rcore`: Investigate alternative embedded platforms and realtime OSs - [ ] `rcore`: Investigate alternative embedded platforms and realtime OSs
- [ ] `rlsw`: Software renderer optimizations: mipmaps, platform-specific SIMD - [ ] `rlsw`: Software renderer optimizations: mipmaps, platform-specific SIMD
- [ ] `rtextures`: Consider moving N-patch system to separate example - [ ] `rtextures`: Consider removing N-patch system, provide as separate example
- [ ] `rtextures`: Review blending modes system, provide more options or better samples - [ ] `rtextures`: Review blending modes system, provide more options or better samples
- [ ] `rtext`: Investigate the recently opened [`Slug`](https://sluglibrary.com/) font rendering algorithm - [ ] `rtext`: Investigate the recently opened [`Slug`](https://sluglibrary.com/) font rendering algorithm
- [ ] `raudio`: Support microphone input, basic API to read microphone - [ ] `raudio`: Support microphone input, basic API to read microphone

View File

@ -2,7 +2,7 @@ const std = @import("std");
const builtin = @import("builtin"); const builtin = @import("builtin");
/// Minimum supported version of Zig /// Minimum supported version of Zig
const min_ver = "0.16.0-dev.2349+204fa8959"; const min_ver = "0.16.0-dev.3013+abd131e33";
const emccOutputDir = "zig-out" ++ std.fs.path.sep_str ++ "htmlout" ++ std.fs.path.sep_str; const emccOutputDir = "zig-out" ++ std.fs.path.sep_str ++ "htmlout" ++ std.fs.path.sep_str;
const emccOutputFile = "index.html"; const emccOutputFile = "index.html";
@ -264,7 +264,10 @@ fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.
setDesktopPlatform(raylib, .android); setDesktopPlatform(raylib, .android);
} else { } else {
try c_source_files.append(b.allocator, "src/rglfw.c"); switch (options.platform) {
.glfw => try c_source_files.append(b.allocator, "src/rglfw.c"),
.rgfw, .sdl, .drm, .android => {},
}
if (options.linux_display_backend == .X11 or options.linux_display_backend == .Both) { if (options.linux_display_backend == .X11 or options.linux_display_backend == .Both) {
raylib.root_module.addCMacro("_GLFW_X11", ""); raylib.root_module.addCMacro("_GLFW_X11", "");
@ -434,6 +437,7 @@ pub const OpenglVersion = enum {
}; };
pub const LinuxDisplayBackend = enum { pub const LinuxDisplayBackend = enum {
None,
X11, X11,
Wayland, Wayland,
Both, Both,

View File

@ -7,8 +7,8 @@
.dependencies = .{ .dependencies = .{
.xcode_frameworks = .{ .xcode_frameworks = .{
.url = "https://pkg.machengine.org/xcode-frameworks/9a45f3ac977fd25dff77e58c6de1870b6808c4a7.tar.gz", .url = "https://pkg.machengine.org/xcode-frameworks/8a1cfb373587ea4c9bb1468b7c986462d8d4e10e.tar.gz",
.hash = "122098b9174895f9708bc824b0f9e550c401892c40a900006459acf2cbf78acd99bb", .hash = "N-V-__8AALShqgXkvqYU6f__FrA22SMWmi2TXCJjNTO1m8XJ",
.lazy = true, .lazy = true,
}, },
.emsdk = .{ .emsdk = .{

View File

@ -1153,7 +1153,10 @@ text/text_font_filters: text/text_font_filters.c
--preload-file text/resources/KAISG.ttf@resources/KAISG.ttf --preload-file text/resources/KAISG.ttf@resources/KAISG.ttf
text/text_font_loading: text/text_font_loading.c text/text_font_loading: text/text_font_loading.c
$(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \
--preload-file text/resources/pixantiqua.fnt@resources/pixantiqua.fnt \
--preload-file text/resources/pixantiqua.png@resources/pixantiqua.png \
--preload-file text/resources/pixantiqua.ttf@resources/pixantiqua.ttf
text/text_font_sdf: text/text_font_sdf.c text/text_font_sdf: text/text_font_sdf.c
$(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \

View File

@ -0,0 +1,238 @@
/*******************************************************************************************
*
* raylib [audio] example - amp envelope
*
* Example complexity rating: [] 1/4
*
* Example originally created with raylib 6.0, last time updated with raylib 6.0
*
* Example contributed by Arbinda Rizki Muhammad (@arbipink) and reviewed by Ramon Santamaria (@raysan5)
*
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software
*
* Copyright (c) 2026 Arbinda Rizki Muhammad (@arbipink)
*
********************************************************************************************/
#include "raylib.h"
#define RAYGUI_IMPLEMENTATION
#include "raygui.h"
#include <math.h> // Required for: sinf()
#define BUFFER_SIZE 4096
#define SAMPLE_RATE 44100
// Wave state
typedef enum {
IDLE,
ATTACK,
DECAY,
SUSTAIN,
RELEASE
} ADSRState;
// Grouping all ADSR parameters and state into a struct
typedef struct {
float attackTime;
float decayTime;
float sustainLevel;
float releaseTime;
float currentValue;
ADSRState state;
} Envelope;
//------------------------------------------------------------------------------------
// Module Functions Declaration
//------------------------------------------------------------------------------------
static void FillAudioBuffer(int i, float *buffer, float envelopeValue, float *audioTime);
static void UpdateEnvelope(Envelope *env);
static void DrawADSRGraph(Envelope *env, Rectangle bounds);
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
int main(void)
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [audio] example - amp envelope");
InitAudioDevice();
// Set the number of samples the stream will keep in memory at a time to BUFFER_SIZE
SetAudioStreamBufferSizeDefault(BUFFER_SIZE);
float buffer[BUFFER_SIZE] = { 0 };
// Init raw audio stream (sample rate: 44100, sample size: 32bit-float, channels: 1-mono)
AudioStream stream = LoadAudioStream(SAMPLE_RATE, 32, 1);
// Init Phase
float audioTime = 0.0f;
// Initialize the struct
Envelope env = {
.attackTime = 1.0f,
.decayTime = 1.0f,
.sustainLevel = 0.5f,
.releaseTime = 1.0f,
.currentValue = 0.0f,
.state = IDLE
};
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
// Main game loop
while (!WindowShouldClose())
{
// Update
//----------------------------------------------------------------------------------
if (IsKeyPressed(KEY_SPACE)) env.state = ATTACK;
if (IsKeyReleased(KEY_SPACE) && (env.state != IDLE)) env.state = RELEASE;
if (IsAudioStreamProcessed(stream))
{
if ((env.state != IDLE) || (env.currentValue > 0.0f))
{
for (int i = 0; i < BUFFER_SIZE; i++)
{
UpdateEnvelope(&env);
FillAudioBuffer(i, buffer, env.currentValue, &audioTime);
}
}
else
{
// Clear buffer if silent to avoid looping noise
for (int i = 0; i < BUFFER_SIZE; i++) buffer[i] = 0;
audioTime = 0.0f;
}
UpdateAudioStream(stream, buffer, BUFFER_SIZE);
}
if (!IsAudioStreamPlaying(stream)) PlayAudioStream(stream);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(RAYWHITE);
GuiSliderBar((Rectangle){ 100, 60, 400, 30 }, "Attack (s)", TextFormat("%2.2fs", env.attackTime), &env.attackTime, 0.1f, 3.0f);
GuiSliderBar((Rectangle){ 100, 100, 400, 30 }, "Decay (s)", TextFormat("%2.2fs", env.decayTime), &env.decayTime, 0.1f, 3.0f);
GuiSliderBar((Rectangle){ 100, 140, 400, 30 }, "Sustain", TextFormat("%2.2f", env.sustainLevel), &env.sustainLevel, 0.0f, 1.0f);
GuiSliderBar((Rectangle){ 100, 180, 400, 30 }, "Release (s)", TextFormat("%2.2fs", env.releaseTime), &env.releaseTime, 0.1f, 3.0f);
DrawADSRGraph(&env, (Rectangle){ 100, 250, 400, 100 });
DrawCircleV((Vector2){ 520, 350 - (env.currentValue * 100) }, 5, MAROON);
DrawText(TextFormat("Current Gain: %2.2f", env.currentValue), 535, 345 - (env.currentValue * 100), 10, MAROON);
DrawText("Press SPACE to PLAY the sound!", 200, 400, 20, LIGHTGRAY);
EndDrawing();
//----------------------------------------------------------------------------------
}
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadAudioStream(stream);
CloseAudioDevice();
CloseWindow();
//--------------------------------------------------------------------------------------
return 0;
}
//------------------------------------------------------------------------------------
// Module Functions Definition
//------------------------------------------------------------------------------------
static void FillAudioBuffer(int i, float *buffer, float envelopeValue, float *audioTime)
{
int frequency = 440;
buffer[i] = envelopeValue*sinf(2.0f*PI*frequency*(*audioTime));
*audioTime += (1.0f/SAMPLE_RATE);
}
static void UpdateEnvelope(Envelope *env)
{
// Calculate the time delta for ONE sample (1/44100)
float sampleTime = 1.0f/SAMPLE_RATE;
switch(env->state)
{
case ATTACK:
{
env->currentValue += (1.0f/env->attackTime)*sampleTime;
if (env->currentValue >= 1.0f)
{
env->currentValue = 1.0f;
env->state = DECAY;
}
} break;
case DECAY:
{
env->currentValue -= ((1.0f - env->sustainLevel)/env->decayTime)*sampleTime;
if (env->currentValue <= env->sustainLevel)
{
env->currentValue = env->sustainLevel;
env->state = SUSTAIN;
}
} break;
case SUSTAIN:
{
env->currentValue = env->sustainLevel;
} break;
case RELEASE:
{
env->currentValue -= (env->sustainLevel/env->releaseTime)*sampleTime;
if (env->currentValue <= 0.001f) // Use a small threshold to avoid infinite tail
{
env->currentValue = 0.0f;
env->state = IDLE;
}
} break;
default: break;
}
}
static void DrawADSRGraph(Envelope *env, Rectangle bounds)
{
DrawRectangleRec(bounds, Fade(LIGHTGRAY, 0.3f));
DrawRectangleLinesEx(bounds, 1, GRAY);
// Fixed visual width for sustain stage since it's an amplitude not a time value
float sustainWidth = 1.0f;
// Total time to visualize (sum of A, D, R + a padding for Sustain)
float totalTime = env->attackTime + env->decayTime + sustainWidth + env->releaseTime;
float scaleX = bounds.width/totalTime;
float scaleY = bounds.height;
Vector2 start = { bounds.x, bounds.y + bounds.height };
Vector2 peak = { start.x + (env->attackTime*scaleX), bounds.y };
Vector2 sustain = { peak.x + (env->decayTime*scaleX), bounds.y + (1.0f - env->sustainLevel)*scaleY };
Vector2 rel = { sustain.x + (sustainWidth*scaleX), sustain.y };
Vector2 end = { rel.x + (env->releaseTime*scaleX), bounds.y + bounds.height };
DrawLineV(start, peak, SKYBLUE);
DrawLineV(peak, sustain, BLUE);
DrawLineV(sustain, rel, DARKBLUE);
DrawLineV(rel, end, ORANGE);
DrawText("ADSR Visualizer", bounds.x, bounds.y - 20, 10, DARKGRAY);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

6051
examples/audio/raygui.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -108,7 +108,7 @@ int main(void)
decalMaterial.maps[MATERIAL_MAP_DIFFUSE].color = RAYWHITE; decalMaterial.maps[MATERIAL_MAP_DIFFUSE].color = RAYWHITE;
bool showModel = true; bool showModel = true;
Model decalModels[MAX_DECALS] = { 0 }; static Model decalModels[MAX_DECALS] = { 0 };
int decalCount = 0; int decalCount = 0;
SetTargetFPS(60); // Set our game to run at 60 frames-per-second SetTargetFPS(60); // Set our game to run at 60 frames-per-second

View File

@ -16,6 +16,7 @@
********************************************************************************************/ ********************************************************************************************/
#include "raylib.h" #include "raylib.h"
#include "rlgl.h"
#include <stdlib.h> // Required for: rand() #include <stdlib.h> // Required for: rand()
#include <math.h> // Required for: cosf(), sinf() #include <math.h> // Required for: cosf(), sinf()
@ -26,8 +27,9 @@
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
// Module Functions Declaration // Module Functions Declaration
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
// Generate mesh using points static Mesh GenMeshPoints(int numPoints); // Generate mesh using points
static Mesh GenMeshPoints(int numPoints); void DrawModelPoints(Model model, Vector3 position, float scale, Color tint); // Draw a model as points
void DrawModelPointsEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model as points with extended parameters
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
// Program main entry point // Program main entry point
@ -184,3 +186,30 @@ static Mesh GenMeshPoints(int numPoints)
return mesh; return mesh;
} }
// Draw a model points
// WARNING: OpenGL ES 2.0 does not support point mode drawing
void DrawModelPoints(Model model, Vector3 position, float scale, Color tint)
{
rlEnablePointMode();
rlDisableBackfaceCulling();
DrawModel(model, position, scale, tint);
rlEnableBackfaceCulling();
rlDisablePointMode();
}
// Draw a model points
// WARNING: OpenGL ES 2.0 does not support point mode drawing
void DrawModelPointsEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint)
{
rlEnablePointMode();
rlDisableBackfaceCulling();
DrawModelEx(model, position, rotationAxis, rotationAngle, scale, tint);
rlEnableBackfaceCulling();
rlDisablePointMode();
}

View File

@ -13,9 +13,9 @@
| models/vox/chr_knight.vox | ❔ | ❔ | - | | models/vox/chr_knight.vox | ❔ | ❔ | - |
| models/vox/chr_sword.vox | ❔ | ❔ | - | | models/vox/chr_sword.vox | ❔ | ❔ | - |
| models/vox/monu9.vox | ❔ | ❔ | - | | models/vox/monu9.vox | ❔ | ❔ | - |
| billboard.png | [@emegeme](https://github.com/emegeme) | [CC0](https://creativecommons.org/publicdomain/zero/1.0/) | - | | billboard.png | [@raysan5](https://github.com/raysan5) | [CC0](https://creativecommons.org/publicdomain/zero/1.0/) | - |
| cubicmap.png | [@raysan5](https://github.com/raysan5) | [CC0](https://creativecommons.org/publicdomain/zero/1.0/) | - | | cubicmap.png | [@raysan5](https://github.com/raysan5) | [CC0](https://creativecommons.org/publicdomain/zero/1.0/) | - |
| cubicmap_atlas.png | [@emegeme](https://github.com/emegeme) | [CC0](https://creativecommons.org/publicdomain/zero/1.0/) | - | | cubicmap_atlas.png | [@raysan5](https://github.com/raysan5) | [CC0](https://creativecommons.org/publicdomain/zero/1.0/) | - |
| heightmap.png | [@raysan5](https://github.com/raysan5) | [CC0](https://creativecommons.org/publicdomain/zero/1.0/) | - | | heightmap.png | [@raysan5](https://github.com/raysan5) | [CC0](https://creativecommons.org/publicdomain/zero/1.0/) | - |
| dresden_square_1k.hdr | [HDRIHaven](https://hdrihaven.com/hdri/?h=dresden_square) | [CC0](https://hdrihaven.com/p/license.php) | - | | dresden_square_1k.hdr | [HDRIHaven](https://hdrihaven.com/hdri/?h=dresden_square) | [CC0](https://hdrihaven.com/p/license.php) | - |
| dresden_square_2k.hdr | [HDRIHaven](https://hdrihaven.com/hdri/?h=dresden_square) | [CC0](https://hdrihaven.com/p/license.php) | - | | dresden_square_2k.hdr | [HDRIHaven](https://hdrihaven.com/hdri/?h=dresden_square) | [CC0](https://hdrihaven.com/p/license.php) | - |

View File

@ -52,7 +52,7 @@ void main()
float specCo = 0.0; float specCo = 0.0;
if (NdotL > 0.0) specCo = pow(max(0.0, dot(viewDir, reflect(-lightDir, normal))), specularExponent); // 16 refers to shine if (NdotL > 0.0) specCo = pow(max(0.0, dot(viewDir, reflect(-lightDir, normal))), specularExponent);
specular += specCo; specular += specCo;

View File

@ -50,7 +50,7 @@ void main()
float specCo = 0.0; float specCo = 0.0;
if (NdotL > 0.0) specCo = pow(max(0.0, dot(viewDir, reflect(-lightDir, normal))), specularExponent); // 16 refers to shine if (NdotL > 0.0) specCo = pow(max(0.0, dot(viewDir, reflect(-lightDir, normal))), specularExponent);
specular += specCo; specular += specCo;

View File

@ -54,7 +54,7 @@ void main()
float specCo = 0.0; float specCo = 0.0;
if (NdotL > 0.0) specCo = pow(max(0.0, dot(viewDir, reflect(-lightDir, normal))), specularExponent); // 16 refers to shine if (NdotL > 0.0) specCo = pow(max(0.0, dot(viewDir, reflect(-lightDir, normal))), specularExponent);
specular += specCo; specular += specCo;

View File

@ -56,6 +56,7 @@ int main(void)
// Set the texture tiling using a shader // Set the texture tiling using a shader
float tiling[2] = { 3.0f, 3.0f }; float tiling[2] = { 3.0f, 3.0f };
Shader shader = LoadShader(0, TextFormat("resources/shaders/glsl%i/tiling.fs", GLSL_VERSION)); Shader shader = LoadShader(0, TextFormat("resources/shaders/glsl%i/tiling.fs", GLSL_VERSION));
SetTextureWrap(texture, TEXTURE_WRAP_REPEAT);
SetShaderValue(shader, GetShaderLocation(shader, "tiling"), tiling, SHADER_UNIFORM_VEC2); SetShaderValue(shader, GetShaderLocation(shader, "tiling"), tiling, SHADER_UNIFORM_VEC2);
model.materials[0].shader = shader; model.materials[0].shader = shader;

View File

@ -38,12 +38,12 @@ int main(void)
// Define characters to draw // Define characters to draw
// NOTE: raylib supports UTF-8 encoding, following list is actually codified as UTF8 internally // NOTE: raylib supports UTF-8 encoding, following list is actually codified as UTF8 internally
const char msg[256] = "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHI\nJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmn\nopqrstuvwxyz{|}~¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓ\nÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷\nøùúûüýþÿ"; const char msg[256] = "!#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHI\nJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmn\nopqrstuvwxyz{|}~¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓ\nÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷\nøùúûüýþÿ";
// NOTE: Textures/Fonts MUST be loaded after Window initialization (OpenGL context is required) // NOTE: Textures/Fonts MUST be loaded after Window initialization (OpenGL context is required)
// BMFont (AngelCode) : Font data and image atlas have been generated using external program // BMFont (AngelCode) : Font data and image atlas have been generated using external program
Font fontBm = LoadFont("resources/pixantiqua.fnt"); Font fontBm = LoadFont("resources/pixantiqua.fnt"); // Requires "resources/pixantiqua.png"
// TTF font : Font data and atlas are generated directly from TTF // TTF font : Font data and atlas are generated directly from TTF
// NOTE: We define a font base size of 32 pixels tall and up-to 250 characters // NOTE: We define a font base size of 32 pixels tall and up-to 250 characters

View File

@ -2960,24 +2960,6 @@
<Param name="Color tint" /> <Param name="Color tint" />
</Overload> </Overload>
</KeyWord> </KeyWord>
<KeyWord name="DrawModelPoints" func="yes">
<Overload retVal="void" descr="Draw a model as points">
<Param name="Model model" />
<Param name="Vector3 position" />
<Param name="float scale" />
<Param name="Color tint" />
</Overload>
</KeyWord>
<KeyWord name="DrawModelPointsEx" func="yes">
<Overload retVal="void" descr="Draw a model as points with extended parameters">
<Param name="Model model" />
<Param name="Vector3 position" />
<Param name="Vector3 rotationAxis" />
<Param name="float rotationAngle" />
<Param name="Vector3 scale" />
<Param name="Color tint" />
</Overload>
</KeyWord>
<KeyWord name="DrawBoundingBox" func="yes"> <KeyWord name="DrawBoundingBox" func="yes">
<Overload retVal="void" descr="Draw bounding box (wires)"> <Overload retVal="void" descr="Draw bounding box (wires)">
<Param name="BoundingBox box" /> <Param name="BoundingBox box" />
@ -3013,7 +2995,7 @@
<Param name="Vector2 size" /> <Param name="Vector2 size" />
<Param name="Vector2 origin" /> <Param name="Vector2 origin" />
<Param name="float rotation" /> <Param name="float rotation" />
<Param name="°ñ_)% " /> <Param name="<EFBFBD><EFBFBD>_)% " />
</Overload> </Overload>
</KeyWord> </KeyWord>

View File

@ -600,8 +600,6 @@ RLAPI void DrawModel(Model model, Vector3 position, float scale, Color tint);
RLAPI void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model with extended parameters RLAPI void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model with extended parameters
RLAPI void DrawModelWires(Model model, Vector3 position, float scale, Color tint); // Draw a model wires (with texture if set) RLAPI void DrawModelWires(Model model, Vector3 position, float scale, Color tint); // Draw a model wires (with texture if set)
RLAPI void DrawModelWiresEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model wires (with texture if set) with extended parameters RLAPI void DrawModelWiresEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model wires (with texture if set) with extended parameters
RLAPI void DrawModelPoints(Model model, Vector3 position, float scale, Color tint); // Draw a model as points
RLAPI void DrawModelPointsEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model as points with extended parameters
RLAPI void DrawBoundingBox(BoundingBox box, Color color); // Draw bounding box (wires) RLAPI void DrawBoundingBox(BoundingBox box, Color color); // Draw bounding box (wires)
RLAPI void DrawBillboard(Camera camera, Texture2D texture, Vector3 position, float scale, Color tint); // Draw a billboard texture RLAPI void DrawBillboard(Camera camera, Texture2D texture, Vector3 position, float scale, Color tint); // Draw a billboard texture
RLAPI void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle source, Vector3 position, Vector2 size, Color tint); // Draw a billboard texture defined by source RLAPI void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle source, Vector3 position, Vector2 size, Color tint); // Draw a billboard texture defined by source

211
src/external/cgltf.h vendored
View File

@ -1,7 +1,7 @@
/** /**
* cgltf - a single-file glTF 2.0 parser written in C99. * cgltf - a single-file glTF 2.0 parser written in C99.
* *
* Version: 1.14 * Version: 1.15
* *
* Website: https://github.com/jkuhlmann/cgltf * Website: https://github.com/jkuhlmann/cgltf
* *
@ -141,7 +141,7 @@ typedef struct cgltf_memory_options
typedef struct cgltf_file_options typedef struct cgltf_file_options
{ {
cgltf_result(*read)(const struct cgltf_memory_options* memory_options, const struct cgltf_file_options* file_options, const char* path, cgltf_size* size, void** data); cgltf_result(*read)(const struct cgltf_memory_options* memory_options, const struct cgltf_file_options* file_options, const char* path, cgltf_size* size, void** data);
void (*release)(const struct cgltf_memory_options* memory_options, const struct cgltf_file_options* file_options, void* data); void (*release)(const struct cgltf_memory_options* memory_options, const struct cgltf_file_options* file_options, void* data, cgltf_size size);
void* user_data; void* user_data;
} cgltf_file_options; } cgltf_file_options;
@ -296,6 +296,7 @@ typedef enum cgltf_meshopt_compression_filter {
cgltf_meshopt_compression_filter_octahedral, cgltf_meshopt_compression_filter_octahedral,
cgltf_meshopt_compression_filter_quaternion, cgltf_meshopt_compression_filter_quaternion,
cgltf_meshopt_compression_filter_exponential, cgltf_meshopt_compression_filter_exponential,
cgltf_meshopt_compression_filter_color,
cgltf_meshopt_compression_filter_max_enum cgltf_meshopt_compression_filter_max_enum
} cgltf_meshopt_compression_filter; } cgltf_meshopt_compression_filter;
@ -308,6 +309,7 @@ typedef struct cgltf_meshopt_compression
cgltf_size count; cgltf_size count;
cgltf_meshopt_compression_mode mode; cgltf_meshopt_compression_mode mode;
cgltf_meshopt_compression_filter filter; cgltf_meshopt_compression_filter filter;
cgltf_bool is_khr;
} cgltf_meshopt_compression; } cgltf_meshopt_compression;
typedef struct cgltf_buffer_view typedef struct cgltf_buffer_view
@ -376,13 +378,29 @@ typedef struct cgltf_image
cgltf_extension* extensions; cgltf_extension* extensions;
} cgltf_image; } cgltf_image;
typedef enum cgltf_filter_type {
cgltf_filter_type_undefined = 0,
cgltf_filter_type_nearest = 9728,
cgltf_filter_type_linear = 9729,
cgltf_filter_type_nearest_mipmap_nearest = 9984,
cgltf_filter_type_linear_mipmap_nearest = 9985,
cgltf_filter_type_nearest_mipmap_linear = 9986,
cgltf_filter_type_linear_mipmap_linear = 9987
} cgltf_filter_type;
typedef enum cgltf_wrap_mode {
cgltf_wrap_mode_clamp_to_edge = 33071,
cgltf_wrap_mode_mirrored_repeat = 33648,
cgltf_wrap_mode_repeat = 10497
} cgltf_wrap_mode;
typedef struct cgltf_sampler typedef struct cgltf_sampler
{ {
char* name; char* name;
cgltf_int mag_filter; cgltf_filter_type mag_filter;
cgltf_int min_filter; cgltf_filter_type min_filter;
cgltf_int wrap_s; cgltf_wrap_mode wrap_s;
cgltf_int wrap_t; cgltf_wrap_mode wrap_t;
cgltf_extras extras; cgltf_extras extras;
cgltf_size extensions_count; cgltf_size extensions_count;
cgltf_extension* extensions; cgltf_extension* extensions;
@ -500,6 +518,14 @@ typedef struct cgltf_iridescence
cgltf_texture_view iridescence_thickness_texture; cgltf_texture_view iridescence_thickness_texture;
} cgltf_iridescence; } cgltf_iridescence;
typedef struct cgltf_diffuse_transmission
{
cgltf_texture_view diffuse_transmission_texture;
cgltf_float diffuse_transmission_factor;
cgltf_float diffuse_transmission_color_factor[3];
cgltf_texture_view diffuse_transmission_color_texture;
} cgltf_diffuse_transmission;
typedef struct cgltf_anisotropy typedef struct cgltf_anisotropy
{ {
cgltf_float anisotropy_strength; cgltf_float anisotropy_strength;
@ -525,6 +551,7 @@ typedef struct cgltf_material
cgltf_bool has_sheen; cgltf_bool has_sheen;
cgltf_bool has_emissive_strength; cgltf_bool has_emissive_strength;
cgltf_bool has_iridescence; cgltf_bool has_iridescence;
cgltf_bool has_diffuse_transmission;
cgltf_bool has_anisotropy; cgltf_bool has_anisotropy;
cgltf_bool has_dispersion; cgltf_bool has_dispersion;
cgltf_pbr_metallic_roughness pbr_metallic_roughness; cgltf_pbr_metallic_roughness pbr_metallic_roughness;
@ -537,6 +564,7 @@ typedef struct cgltf_material
cgltf_volume volume; cgltf_volume volume;
cgltf_emissive_strength emissive_strength; cgltf_emissive_strength emissive_strength;
cgltf_iridescence iridescence; cgltf_iridescence iridescence;
cgltf_diffuse_transmission diffuse_transmission;
cgltf_anisotropy anisotropy; cgltf_anisotropy anisotropy;
cgltf_dispersion dispersion; cgltf_dispersion dispersion;
cgltf_texture_view normal_texture; cgltf_texture_view normal_texture;
@ -743,6 +771,7 @@ typedef struct cgltf_data
{ {
cgltf_file_type file_type; cgltf_file_type file_type;
void* file_data; void* file_data;
cgltf_size file_size;
cgltf_asset asset; cgltf_asset asset;
@ -844,6 +873,8 @@ void cgltf_node_transform_world(const cgltf_node* node, cgltf_float* out_matrix)
const uint8_t* cgltf_buffer_view_data(const cgltf_buffer_view* view); const uint8_t* cgltf_buffer_view_data(const cgltf_buffer_view* view);
const cgltf_accessor* cgltf_find_accessor(const cgltf_primitive* prim, cgltf_attribute_type type, cgltf_int index);
cgltf_bool cgltf_accessor_read_float(const cgltf_accessor* accessor, cgltf_size index, cgltf_float* out, cgltf_size element_size); cgltf_bool cgltf_accessor_read_float(const cgltf_accessor* accessor, cgltf_size index, cgltf_float* out, cgltf_size element_size);
cgltf_bool cgltf_accessor_read_uint(const cgltf_accessor* accessor, cgltf_size index, cgltf_uint* out, cgltf_size element_size); cgltf_bool cgltf_accessor_read_uint(const cgltf_accessor* accessor, cgltf_size index, cgltf_uint* out, cgltf_size element_size);
cgltf_size cgltf_accessor_read_index(const cgltf_accessor* accessor, cgltf_size index); cgltf_size cgltf_accessor_read_index(const cgltf_accessor* accessor, cgltf_size index);
@ -1071,9 +1102,10 @@ static cgltf_result cgltf_default_file_read(const struct cgltf_memory_options* m
return cgltf_result_success; return cgltf_result_success;
} }
static void cgltf_default_file_release(const struct cgltf_memory_options* memory_options, const struct cgltf_file_options* file_options, void* data) static void cgltf_default_file_release(const struct cgltf_memory_options* memory_options, const struct cgltf_file_options* file_options, void* data, cgltf_size size)
{ {
(void)file_options; (void)file_options;
(void)size;
void (*memfree)(void*, void*) = memory_options->free_func ? memory_options->free_func : &cgltf_default_free; void (*memfree)(void*, void*) = memory_options->free_func ? memory_options->free_func : &cgltf_default_free;
memfree(memory_options->user_data, data); memfree(memory_options->user_data, data);
} }
@ -1220,7 +1252,7 @@ cgltf_result cgltf_parse_file(const cgltf_options* options, const char* path, cg
} }
cgltf_result (*file_read)(const struct cgltf_memory_options*, const struct cgltf_file_options*, const char*, cgltf_size*, void**) = options->file.read ? options->file.read : &cgltf_default_file_read; cgltf_result (*file_read)(const struct cgltf_memory_options*, const struct cgltf_file_options*, const char*, cgltf_size*, void**) = options->file.read ? options->file.read : &cgltf_default_file_read;
void (*file_release)(const struct cgltf_memory_options*, const struct cgltf_file_options*, void* data) = options->file.release ? options->file.release : cgltf_default_file_release; void (*file_release)(const struct cgltf_memory_options*, const struct cgltf_file_options*, void* data, cgltf_size size) = options->file.release ? options->file.release : cgltf_default_file_release;
void* file_data = NULL; void* file_data = NULL;
cgltf_size file_size = 0; cgltf_size file_size = 0;
@ -1234,11 +1266,12 @@ cgltf_result cgltf_parse_file(const cgltf_options* options, const char* path, cg
if (result != cgltf_result_success) if (result != cgltf_result_success)
{ {
file_release(&options->memory, &options->file, file_data); file_release(&options->memory, &options->file, file_data, file_size);
return result; return result;
} }
(*out_data)->file_data = file_data; (*out_data)->file_data = file_data;
(*out_data)->file_size = file_size;
return cgltf_result_success; return cgltf_result_success;
} }
@ -1630,8 +1663,8 @@ cgltf_result cgltf_validate(cgltf_data* data)
CGLTF_ASSERT_IF((mc->mode == cgltf_meshopt_compression_mode_triangles || mc->mode == cgltf_meshopt_compression_mode_indices) && mc->filter != cgltf_meshopt_compression_filter_none, cgltf_result_invalid_gltf); CGLTF_ASSERT_IF((mc->mode == cgltf_meshopt_compression_mode_triangles || mc->mode == cgltf_meshopt_compression_mode_indices) && mc->filter != cgltf_meshopt_compression_filter_none, cgltf_result_invalid_gltf);
CGLTF_ASSERT_IF(mc->filter == cgltf_meshopt_compression_filter_octahedral && mc->stride != 4 && mc->stride != 8, cgltf_result_invalid_gltf); CGLTF_ASSERT_IF(mc->filter == cgltf_meshopt_compression_filter_octahedral && mc->stride != 4 && mc->stride != 8, cgltf_result_invalid_gltf);
CGLTF_ASSERT_IF(mc->filter == cgltf_meshopt_compression_filter_quaternion && mc->stride != 8, cgltf_result_invalid_gltf); CGLTF_ASSERT_IF(mc->filter == cgltf_meshopt_compression_filter_quaternion && mc->stride != 8, cgltf_result_invalid_gltf);
CGLTF_ASSERT_IF(mc->filter == cgltf_meshopt_compression_filter_color && mc->stride != 4 && mc->stride != 8, cgltf_result_invalid_gltf);
} }
} }
@ -1822,7 +1855,7 @@ void cgltf_free(cgltf_data* data)
return; return;
} }
void (*file_release)(const struct cgltf_memory_options*, const struct cgltf_file_options*, void* data) = data->file.release ? data->file.release : cgltf_default_file_release; void (*file_release)(const struct cgltf_memory_options*, const struct cgltf_file_options*, void* data, cgltf_size size) = data->file.release ? data->file.release : cgltf_default_file_release;
data->memory.free_func(data->memory.user_data, data->asset.copyright); data->memory.free_func(data->memory.user_data, data->asset.copyright);
data->memory.free_func(data->memory.user_data, data->asset.generator); data->memory.free_func(data->memory.user_data, data->asset.generator);
@ -1857,7 +1890,7 @@ void cgltf_free(cgltf_data* data)
if (data->buffers[i].data_free_method == cgltf_data_free_method_file_release) if (data->buffers[i].data_free_method == cgltf_data_free_method_file_release)
{ {
file_release(&data->memory, &data->file, data->buffers[i].data); file_release(&data->memory, &data->file, data->buffers[i].data, data->buffers[i].size);
} }
else if (data->buffers[i].data_free_method == cgltf_data_free_method_memory_free) else if (data->buffers[i].data_free_method == cgltf_data_free_method_memory_free)
{ {
@ -2096,7 +2129,7 @@ void cgltf_free(cgltf_data* data)
data->memory.free_func(data->memory.user_data, data->extensions_required); data->memory.free_func(data->memory.user_data, data->extensions_required);
file_release(&data->memory, &data->file, data->file_data); file_release(&data->memory, &data->file, data->file_data, data->file_size);
data->memory.free_func(data->memory.user_data, data); data->memory.free_func(data->memory.user_data, data);
} }
@ -2312,11 +2345,58 @@ const uint8_t* cgltf_buffer_view_data(const cgltf_buffer_view* view)
return result; return result;
} }
const cgltf_accessor* cgltf_find_accessor(const cgltf_primitive* prim, cgltf_attribute_type type, cgltf_int index)
{
for (cgltf_size i = 0; i < prim->attributes_count; ++i)
{
const cgltf_attribute* attr = &prim->attributes[i];
if (attr->type == type && attr->index == index)
return attr->data;
}
return NULL;
}
static const uint8_t* cgltf_find_sparse_index(const cgltf_accessor* accessor, cgltf_size needle)
{
const cgltf_accessor_sparse* sparse = &accessor->sparse;
const uint8_t* index_data = cgltf_buffer_view_data(sparse->indices_buffer_view);
const uint8_t* value_data = cgltf_buffer_view_data(sparse->values_buffer_view);
if (index_data == NULL || value_data == NULL)
return NULL;
index_data += sparse->indices_byte_offset;
value_data += sparse->values_byte_offset;
cgltf_size index_stride = cgltf_component_size(sparse->indices_component_type);
cgltf_size offset = 0;
cgltf_size length = sparse->count;
while (length)
{
cgltf_size rem = length % 2;
length /= 2;
cgltf_size index = cgltf_component_read_index(index_data + (offset + length) * index_stride, sparse->indices_component_type);
offset += index < needle ? length + rem : 0;
}
if (offset == sparse->count)
return NULL;
cgltf_size index = cgltf_component_read_index(index_data + offset * index_stride, sparse->indices_component_type);
return index == needle ? value_data + offset * accessor->stride : NULL;
}
cgltf_bool cgltf_accessor_read_float(const cgltf_accessor* accessor, cgltf_size index, cgltf_float* out, cgltf_size element_size) cgltf_bool cgltf_accessor_read_float(const cgltf_accessor* accessor, cgltf_size index, cgltf_float* out, cgltf_size element_size)
{ {
if (accessor->is_sparse) if (accessor->is_sparse)
{ {
return 0; const uint8_t* element = cgltf_find_sparse_index(accessor, index);
if (element)
return cgltf_element_read_float(element, accessor->type, accessor->component_type, accessor->normalized, out, element_size);
} }
if (accessor->buffer_view == NULL) if (accessor->buffer_view == NULL)
{ {
@ -2460,11 +2540,13 @@ cgltf_bool cgltf_accessor_read_uint(const cgltf_accessor* accessor, cgltf_size i
{ {
if (accessor->is_sparse) if (accessor->is_sparse)
{ {
return 0; const uint8_t* element = cgltf_find_sparse_index(accessor, index);
if (element)
return cgltf_element_read_uint(element, accessor->type, accessor->component_type, out, element_size);
} }
if (accessor->buffer_view == NULL) if (accessor->buffer_view == NULL)
{ {
memset(out, 0, element_size * sizeof( cgltf_uint )); memset(out, 0, element_size * sizeof(cgltf_uint));
return 1; return 1;
} }
const uint8_t* element = cgltf_buffer_view_data(accessor->buffer_view); const uint8_t* element = cgltf_buffer_view_data(accessor->buffer_view);
@ -2480,7 +2562,9 @@ cgltf_size cgltf_accessor_read_index(const cgltf_accessor* accessor, cgltf_size
{ {
if (accessor->is_sparse) if (accessor->is_sparse)
{ {
return 0; // This is an error case, but we can't communicate the error with existing interface. const uint8_t* element = cgltf_find_sparse_index(accessor, index);
if (element)
return cgltf_component_read_index(element, accessor->component_type);
} }
if (accessor->buffer_view == NULL) if (accessor->buffer_view == NULL)
{ {
@ -2598,7 +2682,10 @@ cgltf_size cgltf_accessor_unpack_indices(const cgltf_accessor* accessor, void* o
return accessor->count; return accessor->count;
} }
index_count = accessor->count < index_count ? accessor->count : index_count; cgltf_size numbers_per_element = cgltf_num_components(accessor->type);
cgltf_size available_numbers = accessor->count * numbers_per_element;
index_count = available_numbers < index_count ? available_numbers : index_count;
cgltf_size index_component_size = cgltf_component_size(accessor->component_type); cgltf_size index_component_size = cgltf_component_size(accessor->component_type);
if (accessor->is_sparse) if (accessor->is_sparse)
@ -2620,15 +2707,23 @@ cgltf_size cgltf_accessor_unpack_indices(const cgltf_accessor* accessor, void* o
} }
element += accessor->offset; element += accessor->offset;
if (index_component_size == out_component_size && accessor->stride == out_component_size) if (index_component_size == out_component_size && accessor->stride == out_component_size * numbers_per_element)
{ {
memcpy(out, element, index_count * index_component_size); memcpy(out, element, index_count * index_component_size);
return index_count; return index_count;
} }
// Data couldn't be copied with memcpy due to stride being larger than the component size.
// OR
// The component size of the output array is larger than the component size of the index data, so index data will be padded. // The component size of the output array is larger than the component size of the index data, so index data will be padded.
switch (out_component_size) switch (out_component_size)
{ {
case 1:
for (cgltf_size index = 0; index < index_count; index++, element += accessor->stride)
{
((uint8_t*)out)[index] = (uint8_t)cgltf_component_read_index(element, accessor->component_type);
}
break;
case 2: case 2:
for (cgltf_size index = 0; index < index_count; index++, element += accessor->stride) for (cgltf_size index = 0; index < index_count; index++, element += accessor->stride)
{ {
@ -2642,7 +2737,7 @@ cgltf_size cgltf_accessor_unpack_indices(const cgltf_accessor* accessor, void* o
} }
break; break;
default: default:
break; return 0;
} }
return index_count; return index_count;
@ -4278,6 +4373,52 @@ static int cgltf_parse_json_iridescence(cgltf_options* options, jsmntok_t const*
return i; return i;
} }
static int cgltf_parse_json_diffuse_transmission(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_diffuse_transmission* out_diff_transmission)
{
CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
int size = tokens[i].size;
++i;
// Defaults
cgltf_fill_float_array(out_diff_transmission->diffuse_transmission_color_factor, 3, 1.0f);
out_diff_transmission->diffuse_transmission_factor = 0.f;
for (int j = 0; j < size; ++j)
{
CGLTF_CHECK_KEY(tokens[i]);
if (cgltf_json_strcmp(tokens + i, json_chunk, "diffuseTransmissionFactor") == 0)
{
++i;
out_diff_transmission->diffuse_transmission_factor = cgltf_json_to_float(tokens + i, json_chunk);
++i;
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "diffuseTransmissionTexture") == 0)
{
i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_diff_transmission->diffuse_transmission_texture);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "diffuseTransmissionColorFactor") == 0)
{
i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_diff_transmission->diffuse_transmission_color_factor, 3);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "diffuseTransmissionColorTexture") == 0)
{
i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_diff_transmission->diffuse_transmission_color_texture);
}
else
{
i = cgltf_skip_json(tokens, i + 1);
}
if (i < 0)
{
return i;
}
}
return i;
}
static int cgltf_parse_json_anisotropy(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_anisotropy* out_anisotropy) static int cgltf_parse_json_anisotropy(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_anisotropy* out_anisotropy)
{ {
CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
@ -4406,8 +4547,8 @@ static int cgltf_parse_json_sampler(cgltf_options* options, jsmntok_t const* tok
(void)options; (void)options;
CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
out_sampler->wrap_s = 10497; out_sampler->wrap_s = cgltf_wrap_mode_repeat;
out_sampler->wrap_t = 10497; out_sampler->wrap_t = cgltf_wrap_mode_repeat;
int size = tokens[i].size; int size = tokens[i].size;
++i; ++i;
@ -4424,28 +4565,28 @@ static int cgltf_parse_json_sampler(cgltf_options* options, jsmntok_t const* tok
{ {
++i; ++i;
out_sampler->mag_filter out_sampler->mag_filter
= cgltf_json_to_int(tokens + i, json_chunk); = (cgltf_filter_type)cgltf_json_to_int(tokens + i, json_chunk);
++i; ++i;
} }
else if (cgltf_json_strcmp(tokens + i, json_chunk, "minFilter") == 0) else if (cgltf_json_strcmp(tokens + i, json_chunk, "minFilter") == 0)
{ {
++i; ++i;
out_sampler->min_filter out_sampler->min_filter
= cgltf_json_to_int(tokens + i, json_chunk); = (cgltf_filter_type)cgltf_json_to_int(tokens + i, json_chunk);
++i; ++i;
} }
else if (cgltf_json_strcmp(tokens + i, json_chunk, "wrapS") == 0) else if (cgltf_json_strcmp(tokens + i, json_chunk, "wrapS") == 0)
{ {
++i; ++i;
out_sampler->wrap_s out_sampler->wrap_s
= cgltf_json_to_int(tokens + i, json_chunk); = (cgltf_wrap_mode)cgltf_json_to_int(tokens + i, json_chunk);
++i; ++i;
} }
else if (cgltf_json_strcmp(tokens + i, json_chunk, "wrapT") == 0) else if (cgltf_json_strcmp(tokens + i, json_chunk, "wrapT") == 0)
{ {
++i; ++i;
out_sampler->wrap_t out_sampler->wrap_t
= cgltf_json_to_int(tokens + i, json_chunk); = (cgltf_wrap_mode)cgltf_json_to_int(tokens + i, json_chunk);
++i; ++i;
} }
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
@ -4766,6 +4907,11 @@ static int cgltf_parse_json_material(cgltf_options* options, jsmntok_t const* to
out_material->has_iridescence = 1; out_material->has_iridescence = 1;
i = cgltf_parse_json_iridescence(options, tokens, i + 1, json_chunk, &out_material->iridescence); i = cgltf_parse_json_iridescence(options, tokens, i + 1, json_chunk, &out_material->iridescence);
} }
else if (cgltf_json_strcmp(tokens + i, json_chunk, "KHR_materials_diffuse_transmission") == 0)
{
out_material->has_diffuse_transmission = 1;
i = cgltf_parse_json_diffuse_transmission(options, tokens, i + 1, json_chunk, &out_material->diffuse_transmission);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "KHR_materials_anisotropy") == 0) else if (cgltf_json_strcmp(tokens + i, json_chunk, "KHR_materials_anisotropy") == 0)
{ {
out_material->has_anisotropy = 1; out_material->has_anisotropy = 1;
@ -4974,6 +5120,10 @@ static int cgltf_parse_json_meshopt_compression(cgltf_options* options, jsmntok_
{ {
out_meshopt_compression->filter = cgltf_meshopt_compression_filter_exponential; out_meshopt_compression->filter = cgltf_meshopt_compression_filter_exponential;
} }
else if (cgltf_json_strcmp(tokens+i, json_chunk, "COLOR") == 0)
{
out_meshopt_compression->filter = cgltf_meshopt_compression_filter_color;
}
++i; ++i;
} }
else else
@ -5084,6 +5234,12 @@ static int cgltf_parse_json_buffer_view(cgltf_options* options, jsmntok_t const*
out_buffer_view->has_meshopt_compression = 1; out_buffer_view->has_meshopt_compression = 1;
i = cgltf_parse_json_meshopt_compression(options, tokens, i + 1, json_chunk, &out_buffer_view->meshopt_compression); i = cgltf_parse_json_meshopt_compression(options, tokens, i + 1, json_chunk, &out_buffer_view->meshopt_compression);
} }
else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_meshopt_compression") == 0)
{
out_buffer_view->has_meshopt_compression = 1;
out_buffer_view->meshopt_compression.is_khr = 1;
i = cgltf_parse_json_meshopt_compression(options, tokens, i + 1, json_chunk, &out_buffer_view->meshopt_compression);
}
else else
{ {
i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_buffer_view->extensions[out_buffer_view->extensions_count++])); i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_buffer_view->extensions[out_buffer_view->extensions_count++]));
@ -6629,6 +6785,9 @@ static int cgltf_fixup_pointers(cgltf_data* data)
CGLTF_PTRFIXUP(data->materials[i].iridescence.iridescence_texture.texture, data->textures, data->textures_count); CGLTF_PTRFIXUP(data->materials[i].iridescence.iridescence_texture.texture, data->textures, data->textures_count);
CGLTF_PTRFIXUP(data->materials[i].iridescence.iridescence_thickness_texture.texture, data->textures, data->textures_count); CGLTF_PTRFIXUP(data->materials[i].iridescence.iridescence_thickness_texture.texture, data->textures, data->textures_count);
CGLTF_PTRFIXUP(data->materials[i].diffuse_transmission.diffuse_transmission_texture.texture, data->textures, data->textures_count);
CGLTF_PTRFIXUP(data->materials[i].diffuse_transmission.diffuse_transmission_color_texture.texture, data->textures, data->textures_count);
CGLTF_PTRFIXUP(data->materials[i].anisotropy.anisotropy_texture.texture, data->textures, data->textures_count); CGLTF_PTRFIXUP(data->materials[i].anisotropy.anisotropy_texture.texture, data->textures, data->textures_count);
} }

1565
src/external/cgltf_write.h vendored Normal file

File diff suppressed because it is too large Load Diff

306
src/external/dr_flac.h vendored
View File

@ -1,6 +1,6 @@
/* /*
FLAC audio decoder. Choice of public domain or MIT-0. See license statements at the end of this file. FLAC audio decoder. Choice of public domain or MIT-0. See license statements at the end of this file.
dr_flac - v0.13.0 - TBD dr_flac - v0.13.3 - 2026-01-17
David Reid - mackron@gmail.com David Reid - mackron@gmail.com
@ -126,7 +126,7 @@ extern "C" {
#define DRFLAC_VERSION_MAJOR 0 #define DRFLAC_VERSION_MAJOR 0
#define DRFLAC_VERSION_MINOR 13 #define DRFLAC_VERSION_MINOR 13
#define DRFLAC_VERSION_REVISION 0 #define DRFLAC_VERSION_REVISION 3
#define DRFLAC_VERSION_STRING DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MAJOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MINOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_REVISION) #define DRFLAC_VERSION_STRING DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MAJOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MINOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_REVISION)
#include <stddef.h> /* For size_t. */ #include <stddef.h> /* For size_t. */
@ -331,6 +331,12 @@ typedef struct
*/ */
drflac_uint32 type; drflac_uint32 type;
/* The size in bytes of the block and the buffer pointed to by pRawData if it's non-NULL. */
drflac_uint32 rawDataSize;
/* The offset in the stream of the raw data. */
drflac_uint64 rawDataOffset;
/* /*
A pointer to the raw data. This points to a temporary buffer so don't hold on to it. It's best to A pointer to the raw data. This points to a temporary buffer so don't hold on to it. It's best to
not modify the contents of this buffer. Use the structures below for more meaningful and structured not modify the contents of this buffer. Use the structures below for more meaningful and structured
@ -338,9 +344,6 @@ typedef struct
*/ */
const void* pRawData; const void* pRawData;
/* The size in bytes of the block and the buffer pointed to by pRawData if it's non-NULL. */
drflac_uint32 rawDataSize;
union union
{ {
drflac_streaminfo streaminfo; drflac_streaminfo streaminfo;
@ -392,6 +395,7 @@ typedef struct
drflac_uint32 colorDepth; drflac_uint32 colorDepth;
drflac_uint32 indexColorCount; drflac_uint32 indexColorCount;
drflac_uint32 pictureDataSize; drflac_uint32 pictureDataSize;
drflac_uint64 pictureDataOffset; /* Offset from the start of the stream. */
const drflac_uint8* pPictureData; const drflac_uint8* pPictureData;
} picture; } picture;
} data; } data;
@ -2712,9 +2716,17 @@ static DRFLAC_INLINE drflac_uint32 drflac__clz_lzcnt(drflac_cache_t x)
#if defined(__GNUC__) || defined(__clang__) #if defined(__GNUC__) || defined(__clang__)
#if defined(DRFLAC_X64) #if defined(DRFLAC_X64)
{ {
/*
A note on lzcnt.
We check for the presence of the lzcnt instruction at runtime before calling this function, but we still generate this code. I have had
a report where the assembler does not recognize the lzcnt instruction. To work around this we are going to use `rep; bsr` instead which
has an identical byte encoding as lzcnt, and should hopefully improve compatibility with older assemblers.
*/
drflac_uint64 r; drflac_uint64 r;
__asm__ __volatile__ ( __asm__ __volatile__ (
"lzcnt{ %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc" "rep; bsr{q %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc"
/*"lzcnt{ %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc"*/
); );
return (drflac_uint32)r; return (drflac_uint32)r;
@ -2723,12 +2735,13 @@ static DRFLAC_INLINE drflac_uint32 drflac__clz_lzcnt(drflac_cache_t x)
{ {
drflac_uint32 r; drflac_uint32 r;
__asm__ __volatile__ ( __asm__ __volatile__ (
"lzcnt{l %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc" "rep; bsr{l %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc"
/*"lzcnt{l %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc"*/
); );
return r; return r;
} }
#elif defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) && !defined(__ARM_ARCH_6M__) && !defined(DRFLAC_64BIT) /* <-- I haven't tested 64-bit inline assembly, so only enabling this for the 32-bit build for now. */ #elif defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) && !defined(__ARM_ARCH_6M__) && !(defined(__thumb__) && !defined(__thumb2__)) && !defined(DRFLAC_64BIT) /* <-- I haven't tested 64-bit inline assembly, so only enabling this for the 32-bit build for now. */
{ {
unsigned int r; unsigned int r;
__asm__ __volatile__ ( __asm__ __volatile__ (
@ -6434,8 +6447,9 @@ static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, d
runningFilePos += 4; runningFilePos += 4;
metadata.type = blockType; metadata.type = blockType;
metadata.pRawData = NULL;
metadata.rawDataSize = 0; metadata.rawDataSize = 0;
metadata.rawDataOffset = runningFilePos;
metadata.pRawData = NULL;
switch (blockType) switch (blockType)
{ {
@ -6712,59 +6726,151 @@ static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, d
} }
if (onMeta) { if (onMeta) {
void* pRawData; drflac_bool32 result = DRFLAC_TRUE;
const char* pRunningData; drflac_uint32 blockSizeRemaining = blockSize;
const char* pRunningDataEnd; char* pMime = NULL;
char* pDescription = NULL;
void* pPictureData = NULL;
pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks); if (blockSizeRemaining < 4 || onRead(pUserData, &metadata.data.picture.type, 4) != 4) {
if (pRawData == NULL) { result = DRFLAC_FALSE;
return DRFLAC_FALSE; goto done_flac;
}
blockSizeRemaining -= 4;
metadata.data.picture.type = drflac__be2host_32(metadata.data.picture.type);
if (blockSizeRemaining < 4 || onRead(pUserData, &metadata.data.picture.mimeLength, 4) != 4) {
result = DRFLAC_FALSE;
goto done_flac;
}
blockSizeRemaining -= 4;
metadata.data.picture.mimeLength = drflac__be2host_32(metadata.data.picture.mimeLength);
pMime = (char*)drflac__malloc_from_callbacks(metadata.data.picture.mimeLength + 1, pAllocationCallbacks); /* +1 for null terminator. */
if (pMime == NULL) {
result = DRFLAC_FALSE;
goto done_flac;
} }
if (onRead(pUserData, pRawData, blockSize) != blockSize) { if (blockSizeRemaining < metadata.data.picture.mimeLength || onRead(pUserData, pMime, metadata.data.picture.mimeLength) != metadata.data.picture.mimeLength) {
drflac__free_from_callbacks(pRawData, pAllocationCallbacks); result = DRFLAC_FALSE;
return DRFLAC_FALSE; goto done_flac;
}
blockSizeRemaining -= metadata.data.picture.mimeLength;
pMime[metadata.data.picture.mimeLength] = '\0'; /* Null terminate for safety. */
metadata.data.picture.mime = (const char*)pMime;
if (blockSizeRemaining < 4 || onRead(pUserData, &metadata.data.picture.descriptionLength, 4) != 4) {
result = DRFLAC_FALSE;
goto done_flac;
}
blockSizeRemaining -= 4;
metadata.data.picture.descriptionLength = drflac__be2host_32(metadata.data.picture.descriptionLength);
pDescription = (char*)drflac__malloc_from_callbacks(metadata.data.picture.descriptionLength + 1, pAllocationCallbacks); /* +1 for null terminator. */
if (pDescription == NULL) {
result = DRFLAC_FALSE;
goto done_flac;
} }
metadata.pRawData = pRawData; if (blockSizeRemaining < metadata.data.picture.descriptionLength || onRead(pUserData, pDescription, metadata.data.picture.descriptionLength) != metadata.data.picture.descriptionLength) {
metadata.rawDataSize = blockSize; result = DRFLAC_FALSE;
goto done_flac;
pRunningData = (const char*)pRawData;
pRunningDataEnd = (const char*)pRawData + blockSize;
metadata.data.picture.type = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
metadata.data.picture.mimeLength = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
/* Need space for the rest of the block */
if ((pRunningDataEnd - pRunningData) - 24 < (drflac_int64)metadata.data.picture.mimeLength) { /* <-- Note the order of operations to avoid overflow to a valid value */
drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
return DRFLAC_FALSE;
} }
metadata.data.picture.mime = pRunningData; pRunningData += metadata.data.picture.mimeLength; blockSizeRemaining -= metadata.data.picture.descriptionLength;
metadata.data.picture.descriptionLength = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; pDescription[metadata.data.picture.descriptionLength] = '\0'; /* Null terminate for safety. */
metadata.data.picture.description = (const char*)pDescription;
/* Need space for the rest of the block */
if ((pRunningDataEnd - pRunningData) - 20 < (drflac_int64)metadata.data.picture.descriptionLength) { /* <-- Note the order of operations to avoid overflow to a valid value */ if (blockSizeRemaining < 4 || onRead(pUserData, &metadata.data.picture.width, 4) != 4) {
drflac__free_from_callbacks(pRawData, pAllocationCallbacks); result = DRFLAC_FALSE;
return DRFLAC_FALSE; goto done_flac;
} }
metadata.data.picture.description = pRunningData; pRunningData += metadata.data.picture.descriptionLength; blockSizeRemaining -= 4;
metadata.data.picture.width = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; metadata.data.picture.width = drflac__be2host_32(metadata.data.picture.width);
metadata.data.picture.height = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
metadata.data.picture.colorDepth = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
metadata.data.picture.indexColorCount = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
metadata.data.picture.pictureDataSize = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
metadata.data.picture.pPictureData = (const drflac_uint8*)pRunningData;
/* Need space for the picture after the block */ if (blockSizeRemaining < 4 || onRead(pUserData, &metadata.data.picture.height, 4) != 4) {
if (pRunningDataEnd - pRunningData < (drflac_int64)metadata.data.picture.pictureDataSize) { /* <-- Note the order of operations to avoid overflow to a valid value */ result = DRFLAC_FALSE;
drflac__free_from_callbacks(pRawData, pAllocationCallbacks); goto done_flac;
return DRFLAC_FALSE; }
blockSizeRemaining -= 4;
metadata.data.picture.height = drflac__be2host_32(metadata.data.picture.height);
if (blockSizeRemaining < 4 || onRead(pUserData, &metadata.data.picture.colorDepth, 4) != 4) {
result = DRFLAC_FALSE;
goto done_flac;
}
blockSizeRemaining -= 4;
metadata.data.picture.colorDepth = drflac__be2host_32(metadata.data.picture.colorDepth);
if (blockSizeRemaining < 4 || onRead(pUserData, &metadata.data.picture.indexColorCount, 4) != 4) {
result = DRFLAC_FALSE;
goto done_flac;
}
blockSizeRemaining -= 4;
metadata.data.picture.indexColorCount = drflac__be2host_32(metadata.data.picture.indexColorCount);
/* Picture data. */
if (blockSizeRemaining < 4 || onRead(pUserData, &metadata.data.picture.pictureDataSize, 4) != 4) {
result = DRFLAC_FALSE;
goto done_flac;
}
blockSizeRemaining -= 4;
metadata.data.picture.pictureDataSize = drflac__be2host_32(metadata.data.picture.pictureDataSize);
if (blockSizeRemaining < metadata.data.picture.pictureDataSize) {
result = DRFLAC_FALSE;
goto done_flac;
} }
onMeta(pUserDataMD, &metadata); /* For the actual image data we want to store the offset to the start of the stream. */
metadata.data.picture.pictureDataOffset = runningFilePos + (blockSize - blockSizeRemaining);
drflac__free_from_callbacks(pRawData, pAllocationCallbacks); /*
For the allocation of image data, we can allow memory allocation to fail, in which case we just leave
the pointer as null. If it fails, we need to fall back to seeking past the image data.
*/
#ifndef DR_FLAC_NO_PICTURE_METADATA_MALLOC
pPictureData = drflac__malloc_from_callbacks(metadata.data.picture.pictureDataSize, pAllocationCallbacks);
if (pPictureData != NULL) {
if (onRead(pUserData, pPictureData, metadata.data.picture.pictureDataSize) != metadata.data.picture.pictureDataSize) {
result = DRFLAC_FALSE;
goto done_flac;
}
} else
#endif
{
/* Allocation failed. We need to seek past the picture data. */
if (!onSeek(pUserData, metadata.data.picture.pictureDataSize, DRFLAC_SEEK_CUR)) {
result = DRFLAC_FALSE;
goto done_flac;
}
}
blockSizeRemaining -= metadata.data.picture.pictureDataSize;
(void)blockSizeRemaining;
metadata.data.picture.pPictureData = (const drflac_uint8*)pPictureData;
/* Only fire the callback if we actually have a way to read the image data. We must have either a valid offset, or a valid data pointer. */
if (metadata.data.picture.pictureDataOffset != 0 || metadata.data.picture.pPictureData != NULL) {
onMeta(pUserDataMD, &metadata);
} else {
/* Don't have a valid offset or data pointer, so just pretend we don't have a picture metadata. */
}
done_flac:
drflac__free_from_callbacks(pMime, pAllocationCallbacks);
drflac__free_from_callbacks(pDescription, pAllocationCallbacks);
drflac__free_from_callbacks(pPictureData, pAllocationCallbacks);
if (result != DRFLAC_TRUE) {
return DRFLAC_FALSE;
}
} }
} break; } break;
@ -6800,13 +6906,16 @@ static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, d
*/ */
if (onMeta) { if (onMeta) {
void* pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks); void* pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks);
if (pRawData == NULL) { if (pRawData != NULL) {
return DRFLAC_FALSE; if (onRead(pUserData, pRawData, blockSize) != blockSize) {
} drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
return DRFLAC_FALSE;
if (onRead(pUserData, pRawData, blockSize) != blockSize) { }
drflac__free_from_callbacks(pRawData, pAllocationCallbacks); } else {
return DRFLAC_FALSE; /* Allocation failed. We need to seek past the block. */
if (!onSeek(pUserData, blockSize, DRFLAC_SEEK_CUR)) {
return DRFLAC_FALSE;
}
} }
metadata.pRawData = pRawData; metadata.pRawData = pRawData;
@ -8699,7 +8808,7 @@ static drflac_bool32 drflac__on_tell_stdio(void* pUserData, drflac_int64* pCurso
DRFLAC_ASSERT(pFileStdio != NULL); DRFLAC_ASSERT(pFileStdio != NULL);
DRFLAC_ASSERT(pCursor != NULL); DRFLAC_ASSERT(pCursor != NULL);
#if defined(_WIN32) #if defined(_WIN32) && !defined(NXDK)
#if defined(_MSC_VER) && _MSC_VER > 1200 #if defined(_MSC_VER) && _MSC_VER > 1200
result = _ftelli64(pFileStdio); result = _ftelli64(pFileStdio);
#else #else
@ -8821,8 +8930,6 @@ static drflac_bool32 drflac__on_seek_memory(void* pUserData, int offset, drflac_
DRFLAC_ASSERT(memoryStream != NULL); DRFLAC_ASSERT(memoryStream != NULL);
newCursor = memoryStream->currentReadPos;
if (origin == DRFLAC_SEEK_SET) { if (origin == DRFLAC_SEEK_SET) {
newCursor = 0; newCursor = 0;
} else if (origin == DRFLAC_SEEK_CUR) { } else if (origin == DRFLAC_SEEK_CUR) {
@ -11702,58 +11809,43 @@ static type* drflac__full_read_and_close_ ## extension (drflac* pFlac, unsigned
{ \ { \
type* pSampleData = NULL; \ type* pSampleData = NULL; \
drflac_uint64 totalPCMFrameCount; \ drflac_uint64 totalPCMFrameCount; \
type buffer[4096]; \
drflac_uint64 pcmFramesRead; \
size_t sampleDataBufferSize = sizeof(buffer); \
\ \
DRFLAC_ASSERT(pFlac != NULL); \ DRFLAC_ASSERT(pFlac != NULL); \
\ \
totalPCMFrameCount = pFlac->totalPCMFrameCount; \ totalPCMFrameCount = 0; \
\ \
if (totalPCMFrameCount == 0) { \ pSampleData = (type*)drflac__malloc_from_callbacks(sampleDataBufferSize, &pFlac->allocationCallbacks); \
type buffer[4096]; \ if (pSampleData == NULL) { \
drflac_uint64 pcmFramesRead; \ goto on_error; \
size_t sampleDataBufferSize = sizeof(buffer); \ } \
\ \
pSampleData = (type*)drflac__malloc_from_callbacks(sampleDataBufferSize, &pFlac->allocationCallbacks); \ while ((pcmFramesRead = (drflac_uint64)drflac_read_pcm_frames_##extension(pFlac, sizeof(buffer)/sizeof(buffer[0])/pFlac->channels, buffer)) > 0) { \
if (pSampleData == NULL) { \ if (((totalPCMFrameCount + pcmFramesRead) * pFlac->channels * sizeof(type)) > sampleDataBufferSize) { \
goto on_error; \ type* pNewSampleData; \
} \ size_t newSampleDataBufferSize; \
\ \
while ((pcmFramesRead = (drflac_uint64)drflac_read_pcm_frames_##extension(pFlac, sizeof(buffer)/sizeof(buffer[0])/pFlac->channels, buffer)) > 0) { \ newSampleDataBufferSize = sampleDataBufferSize * 2; \
if (((totalPCMFrameCount + pcmFramesRead) * pFlac->channels * sizeof(type)) > sampleDataBufferSize) { \ pNewSampleData = (type*)drflac__realloc_from_callbacks(pSampleData, newSampleDataBufferSize, sampleDataBufferSize, &pFlac->allocationCallbacks); \
type* pNewSampleData; \ if (pNewSampleData == NULL) { \
size_t newSampleDataBufferSize; \ drflac__free_from_callbacks(pSampleData, &pFlac->allocationCallbacks); \
\ goto on_error; \
newSampleDataBufferSize = sampleDataBufferSize * 2; \
pNewSampleData = (type*)drflac__realloc_from_callbacks(pSampleData, newSampleDataBufferSize, sampleDataBufferSize, &pFlac->allocationCallbacks); \
if (pNewSampleData == NULL) { \
drflac__free_from_callbacks(pSampleData, &pFlac->allocationCallbacks); \
goto on_error; \
} \
\
sampleDataBufferSize = newSampleDataBufferSize; \
pSampleData = pNewSampleData; \
} \ } \
\ \
DRFLAC_COPY_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), buffer, (size_t)(pcmFramesRead*pFlac->channels*sizeof(type))); \ sampleDataBufferSize = newSampleDataBufferSize; \
totalPCMFrameCount += pcmFramesRead; \ pSampleData = pNewSampleData; \
} \ } \
\ \
/* At this point everything should be decoded, but we just want to fill the unused part buffer with silence - need to \ DRFLAC_COPY_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), buffer, (size_t)(pcmFramesRead*pFlac->channels*sizeof(type))); \
protect those ears from random noise! */ \ totalPCMFrameCount += pcmFramesRead; \
DRFLAC_ZERO_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), (size_t)(sampleDataBufferSize - totalPCMFrameCount*pFlac->channels*sizeof(type))); \
} else { \
drflac_uint64 dataSize = totalPCMFrameCount*pFlac->channels*sizeof(type); \
if (dataSize > (drflac_uint64)DRFLAC_SIZE_MAX) { \
goto on_error; /* The decoded data is too big. */ \
} \
\
pSampleData = (type*)drflac__malloc_from_callbacks((size_t)dataSize, &pFlac->allocationCallbacks); /* <-- Safe cast as per the check above. */ \
if (pSampleData == NULL) { \
goto on_error; \
} \
\
totalPCMFrameCount = drflac_read_pcm_frames_##extension(pFlac, pFlac->totalPCMFrameCount, pSampleData); \
} \ } \
\ \
/* At this point everything should be decoded, but we just want to fill the unused part buffer with silence - need to \
protect those ears from random noise! */ \
DRFLAC_ZERO_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), (size_t)(sampleDataBufferSize - totalPCMFrameCount*pFlac->channels*sizeof(type))); \
\
if (sampleRateOut) *sampleRateOut = pFlac->sampleRate; \ if (sampleRateOut) *sampleRateOut = pFlac->sampleRate; \
if (channelsOut) *channelsOut = pFlac->channels; \ if (channelsOut) *channelsOut = pFlac->channels; \
if (totalPCMFrameCountOut) *totalPCMFrameCountOut = totalPCMFrameCount; \ if (totalPCMFrameCountOut) *totalPCMFrameCountOut = totalPCMFrameCount; \
@ -12077,7 +12169,19 @@ DRFLAC_API drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterat
/* /*
REVISION HISTORY REVISION HISTORY
================ ================
v0.13.0 - TBD v0.13.3 - 2026-01-17
- Fix a compiler compatibility issue with some inlined assembly.
- Fix a compilation warning.
v0.13.2 - 2025-12-02
- Improve robustness of the parsing of picture metadata to improve support for memory constrained embedded devices.
- Fix a warning about an assigned by unused variable.
- Improvements to drflac_open_and_read_pcm_frames_*() and family to avoid excessively large memory allocations from malformed files.
v0.13.1 - 2025-09-10
- Fix an error with the NXDK build.
v0.13.0 - 2025-07-23
- API CHANGE: Seek origin enums have been renamed to match the naming convention used by other dr_libs libraries: - API CHANGE: Seek origin enums have been renamed to match the naming convention used by other dr_libs libraries:
- drflac_seek_origin_start -> DRFLAC_SEEK_SET - drflac_seek_origin_start -> DRFLAC_SEEK_SET
- drflac_seek_origin_current -> DRFLAC_SEEK_CUR - drflac_seek_origin_current -> DRFLAC_SEEK_CUR

169
src/external/dr_mp3.h vendored
View File

@ -1,6 +1,6 @@
/* /*
MP3 audio decoder. Choice of public domain or MIT-0. See license statements at the end of this file. MP3 audio decoder. Choice of public domain or MIT-0. See license statements at the end of this file.
dr_mp3 - v0.7.0 - TBD dr_mp3 - v0.7.4 - TBD
David Reid - mackron@gmail.com David Reid - mackron@gmail.com
@ -72,7 +72,7 @@ extern "C" {
#define DRMP3_VERSION_MAJOR 0 #define DRMP3_VERSION_MAJOR 0
#define DRMP3_VERSION_MINOR 7 #define DRMP3_VERSION_MINOR 7
#define DRMP3_VERSION_REVISION 0 #define DRMP3_VERSION_REVISION 4
#define DRMP3_VERSION_STRING DRMP3_XSTRINGIFY(DRMP3_VERSION_MAJOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_MINOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_REVISION) #define DRMP3_VERSION_STRING DRMP3_XSTRINGIFY(DRMP3_VERSION_MAJOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_MINOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_REVISION)
#include <stddef.h> /* For size_t. */ #include <stddef.h> /* For size_t. */
@ -257,16 +257,45 @@ typedef struct
Low Level Push API Low Level Push API
================== ==================
*/ */
#define DRMP3_MAX_BITRESERVOIR_BYTES 511
#define DRMP3_MAX_FREE_FORMAT_FRAME_SIZE 2304 /* more than ISO spec's */
#define DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES DRMP3_MAX_FREE_FORMAT_FRAME_SIZE /* MUST be >= 320000/8/32000*1152 = 1440 */
typedef struct typedef struct
{ {
int frame_bytes, channels, sample_rate, layer, bitrate_kbps; int frame_bytes, channels, sample_rate, layer, bitrate_kbps;
} drmp3dec_frame_info; } drmp3dec_frame_info;
typedef struct
{
const drmp3_uint8 *buf;
int pos, limit;
} drmp3_bs;
typedef struct
{
const drmp3_uint8 *sfbtab;
drmp3_uint16 part_23_length, big_values, scalefac_compress;
drmp3_uint8 global_gain, block_type, mixed_block_flag, n_long_sfb, n_short_sfb;
drmp3_uint8 table_select[3], region_count[3], subblock_gain[3];
drmp3_uint8 preflag, scalefac_scale, count1_table, scfsi;
} drmp3_L3_gr_info;
typedef struct
{
drmp3_bs bs;
drmp3_uint8 maindata[DRMP3_MAX_BITRESERVOIR_BYTES + DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES];
drmp3_L3_gr_info gr_info[4];
float grbuf[2][576], scf[40], syn[18 + 15][2*32];
drmp3_uint8 ist_pos[2][39];
} drmp3dec_scratch;
typedef struct typedef struct
{ {
float mdct_overlap[2][9*32], qmf_state[15*2*32]; float mdct_overlap[2][9*32], qmf_state[15*2*32];
int reserv, free_format_bytes; int reserv, free_format_bytes;
drmp3_uint8 header[4], reserv_buf[511]; drmp3_uint8 header[4], reserv_buf[511];
drmp3dec_scratch scratch;
} drmp3dec; } drmp3dec;
/* Initializes a low level decoder. */ /* Initializes a low level decoder. */
@ -592,14 +621,10 @@ DRMP3_API const char* drmp3_version_string(void)
#define DRMP3_OFFSET_PTR(p, offset) ((void*)((drmp3_uint8*)(p) + (offset))) #define DRMP3_OFFSET_PTR(p, offset) ((void*)((drmp3_uint8*)(p) + (offset)))
#define DRMP3_MAX_FREE_FORMAT_FRAME_SIZE 2304 /* more than ISO spec's */
#ifndef DRMP3_MAX_FRAME_SYNC_MATCHES #ifndef DRMP3_MAX_FRAME_SYNC_MATCHES
#define DRMP3_MAX_FRAME_SYNC_MATCHES 10 #define DRMP3_MAX_FRAME_SYNC_MATCHES 10
#endif #endif
#define DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES DRMP3_MAX_FREE_FORMAT_FRAME_SIZE /* MUST be >= 320000/8/32000*1152 = 1440 */
#define DRMP3_MAX_BITRESERVOIR_BYTES 511
#define DRMP3_SHORT_BLOCK_TYPE 2 #define DRMP3_SHORT_BLOCK_TYPE 2
#define DRMP3_STOP_BLOCK_TYPE 3 #define DRMP3_STOP_BLOCK_TYPE 3
#define DRMP3_MODE_MONO 3 #define DRMP3_MODE_MONO 3
@ -632,8 +657,10 @@ DRMP3_API const char* drmp3_version_string(void)
#if !defined(DR_MP3_NO_SIMD) #if !defined(DR_MP3_NO_SIMD)
#if !defined(DR_MP3_ONLY_SIMD) && (defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC)) #if !defined(DR_MP3_ONLY_SIMD) && ((defined(_MSC_VER) && _MSC_VER >= 1400) && defined(_M_X64)) || ((defined(__i386) || defined(_M_IX86) || defined(__i386__) || defined(__x86_64__)) && ((defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__)))
/* x64 always have SSE2, arm64 always have neon, no need for generic code */ #define DR_MP3_ONLY_SIMD
#endif
#if !defined(DR_MP3_ONLY_SIMD) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC))
#define DR_MP3_ONLY_SIMD #define DR_MP3_ONLY_SIMD
#endif #endif
@ -655,7 +682,7 @@ DRMP3_API const char* drmp3_version_string(void)
#define DRMP3_VMUL_S(x, s) _mm_mul_ps(x, _mm_set1_ps(s)) #define DRMP3_VMUL_S(x, s) _mm_mul_ps(x, _mm_set1_ps(s))
#define DRMP3_VREV(x) _mm_shuffle_ps(x, x, _MM_SHUFFLE(0, 1, 2, 3)) #define DRMP3_VREV(x) _mm_shuffle_ps(x, x, _MM_SHUFFLE(0, 1, 2, 3))
typedef __m128 drmp3_f4; typedef __m128 drmp3_f4;
#if defined(_MSC_VER) || defined(DR_MP3_ONLY_SIMD) #if (defined(_MSC_VER) || defined(DR_MP3_ONLY_SIMD)) && !defined(__clang__)
#define drmp3_cpuid __cpuid #define drmp3_cpuid __cpuid
#else #else
static __inline__ __attribute__((always_inline)) void drmp3_cpuid(int CPUInfo[], const int InfoType) static __inline__ __attribute__((always_inline)) void drmp3_cpuid(int CPUInfo[], const int InfoType)
@ -779,11 +806,7 @@ static __inline__ __attribute__((always_inline)) drmp3_int32 drmp3_clip_int16_ar
#define DRMP3_FREE(p) free((p)) #define DRMP3_FREE(p) free((p))
#endif #endif
typedef struct
{
const drmp3_uint8 *buf;
int pos, limit;
} drmp3_bs;
typedef struct typedef struct
{ {
@ -796,24 +819,6 @@ typedef struct
drmp3_uint8 tab_offset, code_tab_width, band_count; drmp3_uint8 tab_offset, code_tab_width, band_count;
} drmp3_L12_subband_alloc; } drmp3_L12_subband_alloc;
typedef struct
{
const drmp3_uint8 *sfbtab;
drmp3_uint16 part_23_length, big_values, scalefac_compress;
drmp3_uint8 global_gain, block_type, mixed_block_flag, n_long_sfb, n_short_sfb;
drmp3_uint8 table_select[3], region_count[3], subblock_gain[3];
drmp3_uint8 preflag, scalefac_scale, count1_table, scfsi;
} drmp3_L3_gr_info;
typedef struct
{
drmp3_bs bs;
drmp3_uint8 maindata[DRMP3_MAX_BITRESERVOIR_BYTES + DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES];
drmp3_L3_gr_info gr_info[4];
float grbuf[2][576], scf[40], syn[18 + 15][2*32];
drmp3_uint8 ist_pos[2][39];
} drmp3dec_scratch;
static void drmp3_bs_init(drmp3_bs *bs, const drmp3_uint8 *data, int bytes) static void drmp3_bs_init(drmp3_bs *bs, const drmp3_uint8 *data, int bytes)
{ {
bs->buf = data; bs->buf = data;
@ -1227,6 +1232,14 @@ static float drmp3_L3_ldexp_q2(float y, int exp_q2)
return y; return y;
} }
/*
I've had reports of GCC 14 throwing an incorrect -Wstringop-overflow warning here. This is an attempt
to silence this warning.
*/
#if (defined(__GNUC__) && (__GNUC__ >= 13)) && !defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstringop-overflow"
#endif
static void drmp3_L3_decode_scalefactors(const drmp3_uint8 *hdr, drmp3_uint8 *ist_pos, drmp3_bs *bs, const drmp3_L3_gr_info *gr, float *scf, int ch) static void drmp3_L3_decode_scalefactors(const drmp3_uint8 *hdr, drmp3_uint8 *ist_pos, drmp3_bs *bs, const drmp3_L3_gr_info *gr, float *scf, int ch)
{ {
static const drmp3_uint8 g_scf_partitions[3][28] = { static const drmp3_uint8 g_scf_partitions[3][28] = {
@ -1288,6 +1301,9 @@ static void drmp3_L3_decode_scalefactors(const drmp3_uint8 *hdr, drmp3_uint8 *is
scf[i] = drmp3_L3_ldexp_q2(gain, iscf[i] << scf_shift); scf[i] = drmp3_L3_ldexp_q2(gain, iscf[i] << scf_shift);
} }
} }
#if (defined(__GNUC__) && (__GNUC__ >= 13)) && !defined(__clang__)
#pragma GCC diagnostic pop
#endif
static const float g_drmp3_pow43[129 + 16] = { static const float g_drmp3_pow43[129 + 16] = {
0,-1,-2.519842f,-4.326749f,-6.349604f,-8.549880f,-10.902724f,-13.390518f,-16.000000f,-18.720754f,-21.544347f,-24.463781f,-27.473142f,-30.567351f,-33.741992f,-36.993181f, 0,-1,-2.519842f,-4.326749f,-6.349604f,-8.549880f,-10.902724f,-13.390518f,-16.000000f,-18.720754f,-21.544347f,-24.463781f,-27.473142f,-30.567351f,-33.741992f,-36.993181f,
@ -2299,7 +2315,6 @@ DRMP3_API int drmp3dec_decode_frame(drmp3dec *dec, const drmp3_uint8 *mp3, int m
int i = 0, igr, frame_size = 0, success = 1; int i = 0, igr, frame_size = 0, success = 1;
const drmp3_uint8 *hdr; const drmp3_uint8 *hdr;
drmp3_bs bs_frame[1]; drmp3_bs bs_frame[1];
drmp3dec_scratch scratch;
if (mp3_bytes > 4 && dec->header[0] == 0xff && drmp3_hdr_compare(dec->header, mp3)) if (mp3_bytes > 4 && dec->header[0] == 0xff && drmp3_hdr_compare(dec->header, mp3))
{ {
@ -2336,23 +2351,23 @@ DRMP3_API int drmp3dec_decode_frame(drmp3dec *dec, const drmp3_uint8 *mp3, int m
if (info->layer == 3) if (info->layer == 3)
{ {
int main_data_begin = drmp3_L3_read_side_info(bs_frame, scratch.gr_info, hdr); int main_data_begin = drmp3_L3_read_side_info(bs_frame, dec->scratch.gr_info, hdr);
if (main_data_begin < 0 || bs_frame->pos > bs_frame->limit) if (main_data_begin < 0 || bs_frame->pos > bs_frame->limit)
{ {
drmp3dec_init(dec); drmp3dec_init(dec);
return 0; return 0;
} }
success = drmp3_L3_restore_reservoir(dec, bs_frame, &scratch, main_data_begin); success = drmp3_L3_restore_reservoir(dec, bs_frame, &dec->scratch, main_data_begin);
if (success && pcm != NULL) if (success && pcm != NULL)
{ {
for (igr = 0; igr < (DRMP3_HDR_TEST_MPEG1(hdr) ? 2 : 1); igr++, pcm = DRMP3_OFFSET_PTR(pcm, sizeof(drmp3d_sample_t)*576*info->channels)) for (igr = 0; igr < (DRMP3_HDR_TEST_MPEG1(hdr) ? 2 : 1); igr++, pcm = DRMP3_OFFSET_PTR(pcm, sizeof(drmp3d_sample_t)*576*info->channels))
{ {
DRMP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); DRMP3_ZERO_MEMORY(dec->scratch.grbuf[0], 576*2*sizeof(float));
drmp3_L3_decode(dec, &scratch, scratch.gr_info + igr*info->channels, info->channels); drmp3_L3_decode(dec, &dec->scratch, dec->scratch.gr_info + igr*info->channels, info->channels);
drmp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 18, info->channels, (drmp3d_sample_t*)pcm, scratch.syn[0]); drmp3d_synth_granule(dec->qmf_state, dec->scratch.grbuf[0], 18, info->channels, (drmp3d_sample_t*)pcm, dec->scratch.syn[0]);
} }
} }
drmp3_L3_save_reservoir(dec, &scratch); drmp3_L3_save_reservoir(dec, &dec->scratch);
} else } else
{ {
#ifdef DR_MP3_ONLY_MP3 #ifdef DR_MP3_ONLY_MP3
@ -2366,15 +2381,15 @@ DRMP3_API int drmp3dec_decode_frame(drmp3dec *dec, const drmp3_uint8 *mp3, int m
drmp3_L12_read_scale_info(hdr, bs_frame, sci); drmp3_L12_read_scale_info(hdr, bs_frame, sci);
DRMP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); DRMP3_ZERO_MEMORY(dec->scratch.grbuf[0], 576*2*sizeof(float));
for (i = 0, igr = 0; igr < 3; igr++) for (i = 0, igr = 0; igr < 3; igr++)
{ {
if (12 == (i += drmp3_L12_dequantize_granule(scratch.grbuf[0] + i, bs_frame, sci, info->layer | 1))) if (12 == (i += drmp3_L12_dequantize_granule(dec->scratch.grbuf[0] + i, bs_frame, sci, info->layer | 1)))
{ {
i = 0; i = 0;
drmp3_L12_apply_scf_384(sci, sci->scf + igr, scratch.grbuf[0]); drmp3_L12_apply_scf_384(sci, sci->scf + igr, dec->scratch.grbuf[0]);
drmp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 12, info->channels, (drmp3d_sample_t*)pcm, scratch.syn[0]); drmp3d_synth_granule(dec->qmf_state, dec->scratch.grbuf[0], 12, info->channels, (drmp3d_sample_t*)pcm, dec->scratch.syn[0]);
DRMP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); DRMP3_ZERO_MEMORY(dec->scratch.grbuf[0], 576*2*sizeof(float));
pcm = DRMP3_OFFSET_PTR(pcm, sizeof(drmp3d_sample_t)*384*info->channels); pcm = DRMP3_OFFSET_PTR(pcm, sizeof(drmp3d_sample_t)*384*info->channels);
} }
if (bs_frame->pos > bs_frame->limit) if (bs_frame->pos > bs_frame->limit)
@ -3005,23 +3020,27 @@ static drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drm
((drmp3_uint32)ape[26] << 16) | ((drmp3_uint32)ape[26] << 16) |
((drmp3_uint32)ape[27] << 24); ((drmp3_uint32)ape[27] << 24);
streamEndOffset -= 32 + tagSize; if (32 + tagSize < streamLen) {
streamLen -= 32 + tagSize; streamEndOffset -= 32 + tagSize;
streamLen -= 32 + tagSize;
/* Fire a metadata callback for the APE data. Must include both the main content and footer. */ /* Fire a metadata callback for the APE data. Must include both the main content and footer. */
if (onMeta != NULL) { if (onMeta != NULL) {
/* We first need to seek to the start of the APE tag. */ /* We first need to seek to the start of the APE tag. */
if (onSeek(pUserData, streamEndOffset, DRMP3_SEEK_END)) { if (onSeek(pUserData, streamEndOffset, DRMP3_SEEK_END)) {
size_t apeTagSize = (size_t)tagSize + 32; size_t apeTagSize = (size_t)tagSize + 32;
drmp3_uint8* pTagData = (drmp3_uint8*)drmp3_malloc(apeTagSize, pAllocationCallbacks); drmp3_uint8* pTagData = (drmp3_uint8*)drmp3_malloc(apeTagSize, pAllocationCallbacks);
if (pTagData != NULL) { if (pTagData != NULL) {
if (onRead(pUserData, pTagData, apeTagSize) == apeTagSize) { if (onRead(pUserData, pTagData, apeTagSize) == apeTagSize) {
drmp3__on_meta(pMP3, DRMP3_METADATA_TYPE_APE, pTagData, apeTagSize); drmp3__on_meta(pMP3, DRMP3_METADATA_TYPE_APE, pTagData, apeTagSize);
}
drmp3_free(pTagData, pAllocationCallbacks);
} }
drmp3_free(pTagData, pAllocationCallbacks);
} }
} }
} else {
/* The tag size is larger than the stream. Invalid APE tag. */
} }
} }
} }
@ -3153,7 +3172,6 @@ static drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drm
{ {
drmp3_bs bs; drmp3_bs bs;
drmp3_L3_gr_info grInfo[4]; drmp3_L3_gr_info grInfo[4];
const drmp3_uint8* pTagData = pFirstFrameData;
drmp3_bs_init(&bs, pFirstFrameData + DRMP3_HDR_SIZE, firstFrameInfo.frame_bytes - DRMP3_HDR_SIZE); drmp3_bs_init(&bs, pFirstFrameData + DRMP3_HDR_SIZE, firstFrameInfo.frame_bytes - DRMP3_HDR_SIZE);
@ -3164,6 +3182,7 @@ static drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drm
if (drmp3_L3_read_side_info(&bs, grInfo, pFirstFrameData) >= 0) { if (drmp3_L3_read_side_info(&bs, grInfo, pFirstFrameData) >= 0) {
drmp3_bool32 isXing = DRMP3_FALSE; drmp3_bool32 isXing = DRMP3_FALSE;
drmp3_bool32 isInfo = DRMP3_FALSE; drmp3_bool32 isInfo = DRMP3_FALSE;
const drmp3_uint8* pTagData;
const drmp3_uint8* pTagDataBeg; const drmp3_uint8* pTagDataBeg;
pTagDataBeg = pFirstFrameData + DRMP3_HDR_SIZE + (bs.pos/8); pTagDataBeg = pFirstFrameData + DRMP3_HDR_SIZE + (bs.pos/8);
@ -3246,6 +3265,13 @@ static drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drm
/* The start offset needs to be moved to the end of this frame so it's not included in any audio processing after seeking. */ /* The start offset needs to be moved to the end of this frame so it's not included in any audio processing after seeking. */
pMP3->streamStartOffset += (drmp3_uint32)(firstFrameInfo.frame_bytes); pMP3->streamStartOffset += (drmp3_uint32)(firstFrameInfo.frame_bytes);
pMP3->streamCursor = pMP3->streamStartOffset; pMP3->streamCursor = pMP3->streamStartOffset;
/*
The internal decoder needs to be reset to clear out any state. If we don't reset this state, it's possible for
there to be inconsistencies in the number of samples read when reading to the end of the stream depending on
whether or not the caller seeks to the start of the stream.
*/
drmp3dec_init(&pMP3->decoder);
} }
} else { } else {
/* Failed to read the side info. */ /* Failed to read the side info. */
@ -3307,8 +3333,6 @@ static drmp3_bool32 drmp3__on_seek_memory(void* pUserData, int byteOffset, drmp3
DRMP3_ASSERT(pMP3 != NULL); DRMP3_ASSERT(pMP3 != NULL);
newCursor = pMP3->memory.currentReadPos;
if (origin == DRMP3_SEEK_SET) { if (origin == DRMP3_SEEK_SET) {
newCursor = 0; newCursor = 0;
} else if (origin == DRMP3_SEEK_CUR) { } else if (origin == DRMP3_SEEK_CUR) {
@ -3981,7 +4005,7 @@ static drmp3_bool32 drmp3__on_tell_stdio(void* pUserData, drmp3_int64* pCursor)
DRMP3_ASSERT(pFileStdio != NULL); DRMP3_ASSERT(pFileStdio != NULL);
DRMP3_ASSERT(pCursor != NULL); DRMP3_ASSERT(pCursor != NULL);
#if defined(_WIN32) #if defined(_WIN32) && !defined(NXDK)
#if defined(_MSC_VER) && _MSC_VER > 1200 #if defined(_MSC_VER) && _MSC_VER > 1200
result = _ftelli64(pFileStdio); result = _ftelli64(pFileStdio);
#else #else
@ -4780,6 +4804,8 @@ static float* drmp3__full_read_and_close_f32(drmp3* pMP3, drmp3_config* pConfig,
pNewFrames = (float*)drmp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks); pNewFrames = (float*)drmp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks);
if (pNewFrames == NULL) { if (pNewFrames == NULL) {
drmp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks); drmp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks);
pFrames = NULL;
totalFramesRead = 0;
break; break;
} }
@ -4847,6 +4873,8 @@ static drmp3_int16* drmp3__full_read_and_close_s16(drmp3* pMP3, drmp3_config* pC
pNewFrames = (drmp3_int16*)drmp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks); pNewFrames = (drmp3_int16*)drmp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks);
if (pNewFrames == NULL) { if (pNewFrames == NULL) {
drmp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks); drmp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks);
pFrames = NULL;
totalFramesRead = 0;
break; break;
} }
@ -4981,7 +5009,24 @@ DIFFERENCES BETWEEN minimp3 AND dr_mp3
/* /*
REVISION HISTORY REVISION HISTORY
================ ================
v0.7.0 - TBD v0.7.4 - TBD
- Improvements to SIMD detection.
v0.7.3 - 2026-01-17
- Fix an error in drmp3_open_and_read_pcm_frames_s16() and family when memory allocation fails.
- Fix some compilation warnings.
v0.7.2 - 2025-12-02
- Reduce stack space to improve robustness on embedded systems.
- Fix a compilation error with MSVC Clang toolset relating to cpuid.
- Fix an error with APE tag parsing.
v0.7.1 - 2025-09-10
- Silence a warning with GCC.
- Fix an error with the NXDK build.
- Fix a decoding inconsistency when seeking. Prior to this change, reading to the end of the stream immediately after initializing will result in a different number of samples read than if the stream is seeked to the start and read to the end.
v0.7.0 - 2025-07-23
- The old `DRMP3_IMPLEMENTATION` has been removed. Use `DR_MP3_IMPLEMENTATION` instead. The reason for this change is that in the future everything will eventually be using the underscored naming convention in the future, so `drmp3` will become `dr_mp3`. - The old `DRMP3_IMPLEMENTATION` has been removed. Use `DR_MP3_IMPLEMENTATION` instead. The reason for this change is that in the future everything will eventually be using the underscored naming convention in the future, so `drmp3` will become `dr_mp3`.
- API CHANGE: Seek origins have been renamed to match the naming convention used by dr_wav and my other libraries. - API CHANGE: Seek origins have been renamed to match the naming convention used by dr_wav and my other libraries.
- drmp3_seek_origin_start -> DRMP3_SEEK_SET - drmp3_seek_origin_start -> DRMP3_SEEK_SET

129
src/external/dr_wav.h vendored
View File

@ -1,6 +1,6 @@
/* /*
WAV audio loader and writer. Choice of public domain or MIT-0. See license statements at the end of this file. WAV audio loader and writer. Choice of public domain or MIT-0. See license statements at the end of this file.
dr_wav - v0.14.0 - TBD dr_wav - v0.14.5 - 2026-03-03
David Reid - mackron@gmail.com David Reid - mackron@gmail.com
@ -147,7 +147,7 @@ extern "C" {
#define DRWAV_VERSION_MAJOR 0 #define DRWAV_VERSION_MAJOR 0
#define DRWAV_VERSION_MINOR 14 #define DRWAV_VERSION_MINOR 14
#define DRWAV_VERSION_REVISION 0 #define DRWAV_VERSION_REVISION 5
#define DRWAV_VERSION_STRING DRWAV_XSTRINGIFY(DRWAV_VERSION_MAJOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_MINOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_REVISION) #define DRWAV_VERSION_STRING DRWAV_XSTRINGIFY(DRWAV_VERSION_MAJOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_MINOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_REVISION)
#include <stddef.h> /* For size_t. */ #include <stddef.h> /* For size_t. */
@ -2189,6 +2189,22 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_smpl_to_metadata_obj(drwav__metadata_pars
if (pMetadata != NULL && bytesJustRead == sizeof(smplHeaderData)) { if (pMetadata != NULL && bytesJustRead == sizeof(smplHeaderData)) {
drwav_uint32 iSampleLoop; drwav_uint32 iSampleLoop;
drwav_uint32 loopCount;
drwav_uint32 calculatedLoopCount;
/*
When we calcualted the amount of memory required for the "smpl" chunk we excluded the chunk entirely
if the loop count in the header did not match with the calculated count based on the size of the
chunk. When this happens, the second stage will still hit this path but the `pMetadata` will be
non-null, but will either be pointing at the very end of the allocation or at the start of another
chunk. We need to check the loop counts for consistency *before* dereferencing the pMetadata object
so it's consistent with how we do it in the first stage.
*/
loopCount = drwav_bytes_to_u32(smplHeaderData + 28);
calculatedLoopCount = (pChunkHeader->sizeInBytes - DRWAV_SMPL_BYTES) / DRWAV_SMPL_LOOP_BYTES;
if (loopCount != calculatedLoopCount) {
return totalBytesRead;
}
pMetadata->type = drwav_metadata_type_smpl; pMetadata->type = drwav_metadata_type_smpl;
pMetadata->data.smpl.manufacturerId = drwav_bytes_to_u32(smplHeaderData + 0); pMetadata->data.smpl.manufacturerId = drwav_bytes_to_u32(smplHeaderData + 0);
@ -2205,7 +2221,7 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_smpl_to_metadata_obj(drwav__metadata_pars
The loop count needs to be validated against the size of the chunk for safety so we don't The loop count needs to be validated against the size of the chunk for safety so we don't
attempt to read over the boundary of the chunk. attempt to read over the boundary of the chunk.
*/ */
if (pMetadata->data.smpl.sampleLoopCount == (pChunkHeader->sizeInBytes - DRWAV_SMPL_BYTES) / DRWAV_SMPL_LOOP_BYTES) { if (pMetadata->data.smpl.sampleLoopCount == calculatedLoopCount) {
pMetadata->data.smpl.pLoops = (drwav_smpl_loop*)drwav__metadata_get_memory(pParser, sizeof(drwav_smpl_loop) * pMetadata->data.smpl.sampleLoopCount, DRWAV_METADATA_ALIGNMENT); pMetadata->data.smpl.pLoops = (drwav_smpl_loop*)drwav__metadata_get_memory(pParser, sizeof(drwav_smpl_loop) * pMetadata->data.smpl.sampleLoopCount, DRWAV_METADATA_ALIGNMENT);
for (iSampleLoop = 0; iSampleLoop < pMetadata->data.smpl.sampleLoopCount; ++iSampleLoop) { for (iSampleLoop = 0; iSampleLoop < pMetadata->data.smpl.sampleLoopCount; ++iSampleLoop) {
@ -2230,6 +2246,15 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_smpl_to_metadata_obj(drwav__metadata_pars
drwav__metadata_parser_read(pParser, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, &totalBytesRead); drwav__metadata_parser_read(pParser, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, &totalBytesRead);
} }
} else {
/*
Getting here means the loop count in the header does not match up with the size of the
chunk. Clear out the data to zero just to be safe.
This should never actually get hit because we check for it above, but keeping this here
for added safety.
*/
DRWAV_ZERO_OBJECT(&pMetadata->data.smpl);
} }
} }
@ -5285,7 +5310,7 @@ DRWAV_PRIVATE drwav_bool32 drwav__on_tell_stdio(void* pUserData, drwav_int64* pC
DRWAV_ASSERT(pFileStdio != NULL); DRWAV_ASSERT(pFileStdio != NULL);
DRWAV_ASSERT(pCursor != NULL); DRWAV_ASSERT(pCursor != NULL);
#if defined(_WIN32) #if defined(_WIN32) && !defined(NXDK)
#if defined(_MSC_VER) && _MSC_VER > 1200 #if defined(_MSC_VER) && _MSC_VER > 1200
result = _ftelli64(pFileStdio); result = _ftelli64(pFileStdio);
#else #else
@ -5492,8 +5517,6 @@ DRWAV_PRIVATE drwav_bool32 drwav__on_seek_memory(void* pUserData, int offset, dr
DRWAV_ASSERT(pWav != NULL); DRWAV_ASSERT(pWav != NULL);
newCursor = pWav->memoryStream.currentReadPos;
if (origin == DRWAV_SEEK_SET) { if (origin == DRWAV_SEEK_SET) {
newCursor = 0; newCursor = 0;
} else if (origin == DRWAV_SEEK_CUR) { } else if (origin == DRWAV_SEEK_CUR) {
@ -5566,8 +5589,6 @@ DRWAV_PRIVATE drwav_bool32 drwav__on_seek_memory_write(void* pUserData, int offs
DRWAV_ASSERT(pWav != NULL); DRWAV_ASSERT(pWav != NULL);
newCursor = pWav->memoryStreamWrite.currentWritePos;
if (origin == DRWAV_SEEK_SET) { if (origin == DRWAV_SEEK_SET) {
newCursor = 0; newCursor = 0;
} else if (origin == DRWAV_SEEK_CUR) { } else if (origin == DRWAV_SEEK_CUR) {
@ -5576,7 +5597,7 @@ DRWAV_PRIVATE drwav_bool32 drwav__on_seek_memory_write(void* pUserData, int offs
newCursor = (drwav_int64)pWav->memoryStreamWrite.dataSize; newCursor = (drwav_int64)pWav->memoryStreamWrite.dataSize;
} else { } else {
DRWAV_ASSERT(!"Invalid seek origin"); DRWAV_ASSERT(!"Invalid seek origin");
return DRWAV_INVALID_ARGS; return DRWAV_FALSE;
} }
newCursor += offset; newCursor += offset;
@ -6258,12 +6279,12 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav
{ {
drwav_uint64 totalFramesRead = 0; drwav_uint64 totalFramesRead = 0;
static drwav_int32 adaptationTable[] = { static const drwav_int32 adaptationTable[] = {
230, 230, 230, 230, 307, 409, 512, 614, 230, 230, 230, 230, 307, 409, 512, 614,
768, 614, 512, 409, 307, 230, 230, 230 768, 614, 512, 409, 307, 230, 230, 230
}; };
static drwav_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460, 392 }; static const drwav_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460, 392 };
static drwav_int32 coeff2Table[] = { 0, -256, 0, 64, 0, -208, -232 }; static const drwav_int32 coeff2Table[] = { 0, -256, 0, 64, 0, -208, -232 };
DRWAV_ASSERT(pWav != NULL); DRWAV_ASSERT(pWav != NULL);
DRWAV_ASSERT(framesToRead > 0); DRWAV_ASSERT(framesToRead > 0);
@ -6292,7 +6313,7 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav
pWav->msadpcm.cachedFrameCount = 2; pWav->msadpcm.cachedFrameCount = 2;
/* The predictor is used as an index into coeff1Table so we'll need to validate to ensure it never overflows. */ /* The predictor is used as an index into coeff1Table so we'll need to validate to ensure it never overflows. */
if (pWav->msadpcm.predictor[0] >= drwav_countof(coeff1Table)) { if (pWav->msadpcm.predictor[0] >= drwav_countof(coeff1Table) || pWav->msadpcm.predictor[0] >= drwav_countof(coeff2Table)) {
return totalFramesRead; /* Invalid file. */ return totalFramesRead; /* Invalid file. */
} }
} else { } else {
@ -6319,7 +6340,8 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav
pWav->msadpcm.cachedFrameCount = 2; pWav->msadpcm.cachedFrameCount = 2;
/* The predictor is used as an index into coeff1Table so we'll need to validate to ensure it never overflows. */ /* The predictor is used as an index into coeff1Table so we'll need to validate to ensure it never overflows. */
if (pWav->msadpcm.predictor[0] >= drwav_countof(coeff1Table) || pWav->msadpcm.predictor[1] >= drwav_countof(coeff2Table)) { if (pWav->msadpcm.predictor[0] >= drwav_countof(coeff1Table) || pWav->msadpcm.predictor[0] >= drwav_countof(coeff2Table) ||
pWav->msadpcm.predictor[1] >= drwav_countof(coeff1Table) || pWav->msadpcm.predictor[1] >= drwav_countof(coeff2Table)) {
return totalFramesRead; /* Invalid file. */ return totalFramesRead; /* Invalid file. */
} }
} }
@ -6373,14 +6395,16 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav
drwav_int32 newSample0; drwav_int32 newSample0;
drwav_int32 newSample1; drwav_int32 newSample1;
/* The predictor is read from the file and then indexed into a table. Check that it's in bounds. */
if (pWav->msadpcm.predictor[0] >= drwav_countof(coeff1Table) || pWav->msadpcm.predictor[0] >= drwav_countof(coeff2Table)) {
return totalFramesRead;
}
newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;
newSample0 += nibble0 * pWav->msadpcm.delta[0]; newSample0 += nibble0 * pWav->msadpcm.delta[0];
newSample0 = drwav_clamp(newSample0, -32768, 32767); newSample0 = drwav_clamp(newSample0, -32768, 32767);
pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8; pWav->msadpcm.delta[0] = (drwav_int32)drwav_clamp(((drwav_int64)adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8, 16, 0x7FFFFFFF);
if (pWav->msadpcm.delta[0] < 16) {
pWav->msadpcm.delta[0] = 16;
}
pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1]; pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1];
pWav->msadpcm.prevFrames[0][1] = newSample0; pWav->msadpcm.prevFrames[0][1] = newSample0;
@ -6390,15 +6414,11 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav
newSample1 += nibble1 * pWav->msadpcm.delta[0]; newSample1 += nibble1 * pWav->msadpcm.delta[0];
newSample1 = drwav_clamp(newSample1, -32768, 32767); newSample1 = drwav_clamp(newSample1, -32768, 32767);
pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[0]) >> 8; pWav->msadpcm.delta[0] = (drwav_int32)drwav_clamp(((drwav_int64)adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[0]) >> 8, 16, 0x7FFFFFFF);
if (pWav->msadpcm.delta[0] < 16) {
pWav->msadpcm.delta[0] = 16;
}
pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1]; pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1];
pWav->msadpcm.prevFrames[0][1] = newSample1; pWav->msadpcm.prevFrames[0][1] = newSample1;
pWav->msadpcm.cachedFrames[2] = newSample0; pWav->msadpcm.cachedFrames[2] = newSample0;
pWav->msadpcm.cachedFrames[3] = newSample1; pWav->msadpcm.cachedFrames[3] = newSample1;
pWav->msadpcm.cachedFrameCount = 2; pWav->msadpcm.cachedFrameCount = 2;
@ -6408,28 +6428,30 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav
drwav_int32 newSample1; drwav_int32 newSample1;
/* Left. */ /* Left. */
if (pWav->msadpcm.predictor[0] >= drwav_countof(coeff1Table) || pWav->msadpcm.predictor[0] >= drwav_countof(coeff2Table)) {
return totalFramesRead; /* Out of bounds. Invalid file. */
}
newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;
newSample0 += nibble0 * pWav->msadpcm.delta[0]; newSample0 += nibble0 * pWav->msadpcm.delta[0];
newSample0 = drwav_clamp(newSample0, -32768, 32767); newSample0 = drwav_clamp(newSample0, -32768, 32767);
pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8; pWav->msadpcm.delta[0] = (drwav_int32)drwav_clamp(((drwav_int64)adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8, 16, 0x7FFFFFFF);
if (pWav->msadpcm.delta[0] < 16) {
pWav->msadpcm.delta[0] = 16;
}
pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1]; pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1];
pWav->msadpcm.prevFrames[0][1] = newSample0; pWav->msadpcm.prevFrames[0][1] = newSample0;
/* Right. */ /* Right. */
if (pWav->msadpcm.predictor[1] >= drwav_countof(coeff1Table) || pWav->msadpcm.predictor[1] >= drwav_countof(coeff2Table)) {
return totalFramesRead; /* Out of bounds. Invalid file. */
}
newSample1 = ((pWav->msadpcm.prevFrames[1][1] * coeff1Table[pWav->msadpcm.predictor[1]]) + (pWav->msadpcm.prevFrames[1][0] * coeff2Table[pWav->msadpcm.predictor[1]])) >> 8; newSample1 = ((pWav->msadpcm.prevFrames[1][1] * coeff1Table[pWav->msadpcm.predictor[1]]) + (pWav->msadpcm.prevFrames[1][0] * coeff2Table[pWav->msadpcm.predictor[1]])) >> 8;
newSample1 += nibble1 * pWav->msadpcm.delta[1]; newSample1 += nibble1 * pWav->msadpcm.delta[1];
newSample1 = drwav_clamp(newSample1, -32768, 32767); newSample1 = drwav_clamp(newSample1, -32768, 32767);
pWav->msadpcm.delta[1] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[1]) >> 8; pWav->msadpcm.delta[1] = (drwav_int32)drwav_clamp(((drwav_int64)adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[1]) >> 8, 16, 0x7FFFFFFF);
if (pWav->msadpcm.delta[1] < 16) {
pWav->msadpcm.delta[1] = 16;
}
pWav->msadpcm.prevFrames[1][0] = pWav->msadpcm.prevFrames[1][1]; pWav->msadpcm.prevFrames[1][0] = pWav->msadpcm.prevFrames[1][1];
pWav->msadpcm.prevFrames[1][1] = newSample1; pWav->msadpcm.prevFrames[1][1] = newSample1;
@ -6451,12 +6473,12 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uin
drwav_uint64 totalFramesRead = 0; drwav_uint64 totalFramesRead = 0;
drwav_uint32 iChannel; drwav_uint32 iChannel;
static drwav_int32 indexTable[16] = { static const drwav_int32 indexTable[16] = {
-1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8,
-1, -1, -1, -1, 2, 4, 6, 8 -1, -1, -1, -1, 2, 4, 6, 8
}; };
static drwav_int32 stepTable[89] = { static const drwav_int32 stepTable[89] = {
7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
@ -6606,7 +6628,7 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uin
#ifndef DR_WAV_NO_CONVERSION_API #ifndef DR_WAV_NO_CONVERSION_API
static unsigned short g_drwavAlawTable[256] = { static const unsigned short g_drwavAlawTable[256] = {
0xEA80, 0xEB80, 0xE880, 0xE980, 0xEE80, 0xEF80, 0xEC80, 0xED80, 0xE280, 0xE380, 0xE080, 0xE180, 0xE680, 0xE780, 0xE480, 0xE580, 0xEA80, 0xEB80, 0xE880, 0xE980, 0xEE80, 0xEF80, 0xEC80, 0xED80, 0xE280, 0xE380, 0xE080, 0xE180, 0xE680, 0xE780, 0xE480, 0xE580,
0xF540, 0xF5C0, 0xF440, 0xF4C0, 0xF740, 0xF7C0, 0xF640, 0xF6C0, 0xF140, 0xF1C0, 0xF040, 0xF0C0, 0xF340, 0xF3C0, 0xF240, 0xF2C0, 0xF540, 0xF5C0, 0xF440, 0xF4C0, 0xF740, 0xF7C0, 0xF640, 0xF6C0, 0xF140, 0xF1C0, 0xF040, 0xF0C0, 0xF340, 0xF3C0, 0xF240, 0xF2C0,
0xAA00, 0xAE00, 0xA200, 0xA600, 0xBA00, 0xBE00, 0xB200, 0xB600, 0x8A00, 0x8E00, 0x8200, 0x8600, 0x9A00, 0x9E00, 0x9200, 0x9600, 0xAA00, 0xAE00, 0xA200, 0xA600, 0xBA00, 0xBE00, 0xB200, 0xB600, 0x8A00, 0x8E00, 0x8200, 0x8600, 0x9A00, 0x9E00, 0x9200, 0x9600,
@ -6625,7 +6647,7 @@ static unsigned short g_drwavAlawTable[256] = {
0x02B0, 0x0290, 0x02F0, 0x02D0, 0x0230, 0x0210, 0x0270, 0x0250, 0x03B0, 0x0390, 0x03F0, 0x03D0, 0x0330, 0x0310, 0x0370, 0x0350 0x02B0, 0x0290, 0x02F0, 0x02D0, 0x0230, 0x0210, 0x0270, 0x0250, 0x03B0, 0x0390, 0x03F0, 0x03D0, 0x0330, 0x0310, 0x0370, 0x0350
}; };
static unsigned short g_drwavMulawTable[256] = { static const unsigned short g_drwavMulawTable[256] = {
0x8284, 0x8684, 0x8A84, 0x8E84, 0x9284, 0x9684, 0x9A84, 0x9E84, 0xA284, 0xA684, 0xAA84, 0xAE84, 0xB284, 0xB684, 0xBA84, 0xBE84, 0x8284, 0x8684, 0x8A84, 0x8E84, 0x9284, 0x9684, 0x9A84, 0x9E84, 0xA284, 0xA684, 0xAA84, 0xAE84, 0xB284, 0xB684, 0xBA84, 0xBE84,
0xC184, 0xC384, 0xC584, 0xC784, 0xC984, 0xCB84, 0xCD84, 0xCF84, 0xD184, 0xD384, 0xD584, 0xD784, 0xD984, 0xDB84, 0xDD84, 0xDF84, 0xC184, 0xC384, 0xC584, 0xC784, 0xC984, 0xCB84, 0xCD84, 0xCF84, 0xD184, 0xD384, 0xD584, 0xD784, 0xD984, 0xDB84, 0xDD84, 0xDF84,
0xE104, 0xE204, 0xE304, 0xE404, 0xE504, 0xE604, 0xE704, 0xE804, 0xE904, 0xEA04, 0xEB04, 0xEC04, 0xED04, 0xEE04, 0xEF04, 0xF004, 0xE104, 0xE204, 0xE304, 0xE404, 0xE504, 0xE604, 0xE704, 0xE804, 0xE904, 0xEA04, 0xEB04, 0xEC04, 0xED04, 0xEE04, 0xEF04, 0xF004,
@ -8053,6 +8075,12 @@ DRWAV_PRIVATE drwav_int16* drwav__read_pcm_frames_and_close_s16(drwav* pWav, uns
DRWAV_ASSERT(pWav != NULL); DRWAV_ASSERT(pWav != NULL);
/* Check for overflow before multiplication. */
if (pWav->channels == 0 || pWav->totalPCMFrameCount > DRWAV_SIZE_MAX / pWav->channels / sizeof(drwav_int16)) {
drwav_uninit(pWav);
return NULL; /* Overflow or invalid channels. */
}
sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(drwav_int16); sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(drwav_int16);
if (sampleDataSize > DRWAV_SIZE_MAX) { if (sampleDataSize > DRWAV_SIZE_MAX) {
drwav_uninit(pWav); drwav_uninit(pWav);
@ -8095,6 +8123,12 @@ DRWAV_PRIVATE float* drwav__read_pcm_frames_and_close_f32(drwav* pWav, unsigned
DRWAV_ASSERT(pWav != NULL); DRWAV_ASSERT(pWav != NULL);
/* Check for overflow before multiplication. */
if (pWav->channels == 0 || pWav->totalPCMFrameCount > DRWAV_SIZE_MAX / pWav->channels / sizeof(float)) {
drwav_uninit(pWav);
return NULL; /* Overflow or invalid channels. */
}
sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(float); sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(float);
if (sampleDataSize > DRWAV_SIZE_MAX) { if (sampleDataSize > DRWAV_SIZE_MAX) {
drwav_uninit(pWav); drwav_uninit(pWav);
@ -8137,6 +8171,12 @@ DRWAV_PRIVATE drwav_int32* drwav__read_pcm_frames_and_close_s32(drwav* pWav, uns
DRWAV_ASSERT(pWav != NULL); DRWAV_ASSERT(pWav != NULL);
/* Check for overflow before multiplication. */
if (pWav->channels == 0 || pWav->totalPCMFrameCount > DRWAV_SIZE_MAX / pWav->channels / sizeof(drwav_int32)) {
drwav_uninit(pWav);
return NULL; /* Overflow or invalid channels. */
}
sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(drwav_int32); sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(drwav_int32);
if (sampleDataSize > DRWAV_SIZE_MAX) { if (sampleDataSize > DRWAV_SIZE_MAX) {
drwav_uninit(pWav); drwav_uninit(pWav);
@ -8517,7 +8557,24 @@ DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b)
/* /*
REVISION HISTORY REVISION HISTORY
================ ================
v0.14.0 - TBD v0.14.5 - 2026-03-03
- Fix a crash when loading files with a malformed "smpl" chunk.
- Fix a signed overflow bug with the MS-ADPCM decoder.
v0.14.4 - 2026-01-17
- Fix some compilation warnings.
v0.14.3 - 2025-12-14
- Fix a possible out-of-bounds read when reading from MS-ADPCM encoded files.
- Fix a possible integer overflow error.
v0.14.2 - 2025-12-02
- Fix a compilation warning.
v0.14.1 - 2025-09-10
- Fix an error with the NXDK build.
v0.14.0 - 2025-07-23
- API CHANGE: Seek origin enums have been renamed to the following: - API CHANGE: Seek origin enums have been renamed to the following:
- drwav_seek_origin_start -> DRWAV_SEEK_SET - drwav_seek_origin_start -> DRWAV_SEEK_SET
- drwav_seek_origin_current -> DRWAV_SEEK_CUR - drwav_seek_origin_current -> DRWAV_SEEK_CUR

53
src/external/m3d.h vendored
View File

@ -236,7 +236,8 @@ typedef struct {
#ifdef M3D_ASCII #ifdef M3D_ASCII
#define M3D_PROPERTYDEF(f,i,n) { (f), (i), (char*)(n) } #define M3D_PROPERTYDEF(f,i,n) { (f), (i), (char*)(n) }
char *key; char *key;
#else #endif
#ifndef M3D_ASCII
#define M3D_PROPERTYDEF(f,i,n) { (f), (i) } #define M3D_PROPERTYDEF(f,i,n) { (f), (i) }
#endif #endif
} m3dpd_t; } m3dpd_t;
@ -414,7 +415,8 @@ typedef struct {
#ifdef M3D_ASCII #ifdef M3D_ASCII
#define M3D_CMDDEF(t,n,p,a,b,c,d,e,f,g,h) { (char*)(n), (p), { (a), (b), (c), (d), (e), (f), (g), (h) } } #define M3D_CMDDEF(t,n,p,a,b,c,d,e,f,g,h) { (char*)(n), (p), { (a), (b), (c), (d), (e), (f), (g), (h) } }
char *key; char *key;
#else #endif
#ifndef M3D_ASCII
#define M3D_CMDDEF(t,n,p,a,b,c,d,e,f,g,h) { (p), { (a), (b), (c), (d), (e), (f), (g), (h) } } #define M3D_CMDDEF(t,n,p,a,b,c,d,e,f,g,h) { (p), { (a), (b), (c), (d), (e), (f), (g), (h) } }
#endif #endif
uint8_t p; uint8_t p;
@ -674,6 +676,10 @@ static m3dcd_t m3d_commandtypes[] = {
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
/* we'll need this with M3D_NOTEXTURE */
char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header);
#ifndef M3D_NOTEXTURE
#if !defined(M3D_NOIMPORTER) && !defined(STBI_INCLUDE_STB_IMAGE_H) #if !defined(M3D_NOIMPORTER) && !defined(STBI_INCLUDE_STB_IMAGE_H)
/* PNG decompressor from /* PNG decompressor from
@ -1868,6 +1874,11 @@ static void *_m3dstbi__png_load(_m3dstbi__context *s, int *x, int *y, int *comp,
#if !defined(M3D_NOIMPORTER) && defined(STBI_INCLUDE_STB_IMAGE_H) && !defined(STB_IMAGE_IMPLEMENTATION) #if !defined(M3D_NOIMPORTER) && defined(STBI_INCLUDE_STB_IMAGE_H) && !defined(STB_IMAGE_IMPLEMENTATION)
#error "stb_image.h included without STB_IMAGE_IMPLEMENTATION. Sorry, we need some stuff defined inside the ifguard for proper integration" #error "stb_image.h included without STB_IMAGE_IMPLEMENTATION. Sorry, we need some stuff defined inside the ifguard for proper integration"
#endif #endif
#else
#if !defined(STBI_INCLUDE_STB_IMAGE_H) || defined(STBI_NO_ZLIB)
#error "stb_image.h not included or STBI_NO_ZLIB defined. Sorry, we need its zlib implementation for proper integration"
#endif
#endif /* M3D_NOTEXTURE */
#if defined(M3D_EXPORTER) && !defined(INCLUDE_STB_IMAGE_WRITE_H) #if defined(M3D_EXPORTER) && !defined(INCLUDE_STB_IMAGE_WRITE_H)
/* zlib_compressor from /* zlib_compressor from
@ -2168,9 +2179,11 @@ M3D_INDEX _m3d_gettx(m3d_t *model, m3dread_t readfilecb, m3dfree_t freecb, char
unsigned int i, len = 0; unsigned int i, len = 0;
unsigned char *buff = NULL; unsigned char *buff = NULL;
char *fn2; char *fn2;
#ifndef M3D_NOTEXTURE
unsigned int w, h; unsigned int w, h;
stbi__context s; stbi__context s;
stbi__result_info ri; stbi__result_info ri;
#endif
/* failsafe */ /* failsafe */
if(!fn || !*fn) return M3D_UNDEF; if(!fn || !*fn) return M3D_UNDEF;
@ -2209,13 +2222,17 @@ M3D_INDEX _m3d_gettx(m3d_t *model, m3dread_t readfilecb, m3dfree_t freecb, char
if(!model->texture) { if(!model->texture) {
if(buff && freecb) (*freecb)(buff); if(buff && freecb) (*freecb)(buff);
model->errcode = M3D_ERR_ALLOC; model->errcode = M3D_ERR_ALLOC;
model->numtexture = 0;
return M3D_UNDEF; return M3D_UNDEF;
} }
memset(&model->texture[i], 0, sizeof(m3dtx_t));
model->texture[i].name = fn; model->texture[i].name = fn;
model->texture[i].w = model->texture[i].h = 0; model->texture[i].d = NULL;
if(buff) { if(buff) {
if(buff[0] == 0x89 && buff[1] == 'P' && buff[2] == 'N' && buff[3] == 'G') { if(buff[0] == 0x89 && buff[1] == 'P' && buff[2] == 'N' && buff[3] == 'G') {
s.read_from_callbacks = 0; #ifndef M3D_NOTEXTURE
/* return pixel buffer of the decoded texture */
memset(&s, 0, sizeof(s));
memset(&ri, 0, sizeof(ri));
s.img_buffer = s.img_buffer_original = (unsigned char *) buff; s.img_buffer = s.img_buffer_original = (unsigned char *) buff;
s.img_buffer_end = s.img_buffer_original_end = (unsigned char *) buff+len; s.img_buffer_end = s.img_buffer_original_end = (unsigned char *) buff+len;
/* don't use model->texture[i].w directly, it's a uint16_t */ /* don't use model->texture[i].w directly, it's a uint16_t */
@ -2225,6 +2242,16 @@ M3D_INDEX _m3d_gettx(m3d_t *model, m3dread_t readfilecb, m3dfree_t freecb, char
model->texture[i].w = w; model->texture[i].w = w;
model->texture[i].h = h; model->texture[i].h = h;
model->texture[i].f = (uint8_t)len; model->texture[i].f = (uint8_t)len;
#else
/* return only the raw undecoded texture */
if((model->texture[i].d = (uint8_t*)M3D_MALLOC(len))) {
memcpy(model->texture[i].d, buff, len);
model->texture[i].w = len & 0xffff;
model->texture[i].h = (len >> 16) & 0xffff;
model->texture[i].f = 0;
} else
model->errcode = M3D_ERR_ALLOC;
#endif
} else { } else {
#ifdef M3D_TX_INTERP #ifdef M3D_TX_INTERP
if((model->errcode = M3D_TX_INTERP(fn, buff, len, &model->texture[i])) != M3D_SUCCESS) { if((model->errcode = M3D_TX_INTERP(fn, buff, len, &model->texture[i])) != M3D_SUCCESS) {
@ -4280,7 +4307,7 @@ m3db_t *m3d_pose(m3d_t *model, M3D_INDEX actionid, uint32_t msec)
if(l != msec) { if(l != msec) {
model->vertex = (m3dv_t*)M3D_REALLOC(model->vertex, (model->numvertex + 2 * model->numbone) * sizeof(m3dv_t)); model->vertex = (m3dv_t*)M3D_REALLOC(model->vertex, (model->numvertex + 2 * model->numbone) * sizeof(m3dv_t));
if(!model->vertex) { if(!model->vertex) {
free(ret); M3D_FREE(ret);
model->errcode = M3D_ERR_ALLOC; model->errcode = M3D_ERR_ALLOC;
return NULL; return NULL;
} }
@ -4492,7 +4519,7 @@ void m3d_free(m3d_t *model)
if(model->label) M3D_FREE(model->label); if(model->label) M3D_FREE(model->label);
if(model->inlined) M3D_FREE(model->inlined); if(model->inlined) M3D_FREE(model->inlined);
if(model->extra) M3D_FREE(model->extra); if(model->extra) M3D_FREE(model->extra);
free(model); M3D_FREE(model);
} }
#endif #endif
@ -4568,15 +4595,15 @@ static uint32_t _m3d_stridx(m3dstr_t *str, uint32_t numstr, char *s)
safe = _m3d_safestr(s, 0); safe = _m3d_safestr(s, 0);
if(!safe) return 0; if(!safe) return 0;
if(!*safe) { if(!*safe) {
free(safe); M3D_FREE(safe);
return 0; return 0;
} }
for(i = 0; i < numstr; i++) for(i = 0; i < numstr; i++)
if(!strcmp(str[i].str, s)) { if(!strcmp(str[i].str, s)) {
free(safe); M3D_FREE(safe);
return str[i].offs; return str[i].offs;
} }
free(safe); M3D_FREE(safe);
} }
return 0; return 0;
} }
@ -4889,7 +4916,7 @@ unsigned char *m3d_save(m3d_t *model, int quality, int flags, unsigned int *size
if(cmd->type == m3dc_mesh) { if(cmd->type == m3dc_mesh) {
if(numgrp + 2 < maxgrp) { if(numgrp + 2 < maxgrp) {
maxgrp += 1024; maxgrp += 1024;
grpidx = (uint32_t*)realloc(grpidx, maxgrp * sizeof(uint32_t)); grpidx = (uint32_t*)M3D_REALLOC(grpidx, maxgrp * sizeof(uint32_t));
if(!grpidx) goto memerr; if(!grpidx) goto memerr;
if(!numgrp) { if(!numgrp) {
grpidx[0] = 0; grpidx[0] = 0;
@ -5194,7 +5221,7 @@ memerr: if(vrtxidx) M3D_FREE(vrtxidx);
if(sa) M3D_FREE(sa); if(sa) M3D_FREE(sa);
if(sd) M3D_FREE(sd); if(sd) M3D_FREE(sd);
if(out) M3D_FREE(out); if(out) M3D_FREE(out);
if(opa) free(opa); if(opa) M3D_FREE(opa);
if(h) M3D_FREE(h); if(h) M3D_FREE(h);
M3D_LOG("Out of memory"); M3D_LOG("Out of memory");
model->errcode = M3D_ERR_ALLOC; model->errcode = M3D_ERR_ALLOC;
@ -6285,7 +6312,7 @@ memerr: if(vrtxidx) M3D_FREE(vrtxidx);
if(skin) M3D_FREE(skin); if(skin) M3D_FREE(skin);
if(str) M3D_FREE(str); if(str) M3D_FREE(str);
if(vrtx) M3D_FREE(vrtx); if(vrtx) M3D_FREE(vrtx);
if(opa) free(opa); if(opa) M3D_FREE(opa);
if(h) M3D_FREE(h); if(h) M3D_FREE(h);
return out; return out;
} }
@ -6310,7 +6337,7 @@ namespace M3D {
public: public:
Model() { Model() {
this->model = (m3d_t*)malloc(sizeof(m3d_t)); memset(this->model, 0, sizeof(m3d_t)); this->model = (m3d_t*)M3D_MALLOC(sizeof(m3d_t)); memset(this->model, 0, sizeof(m3d_t));
} }
Model(_unused const std::string &data, _unused m3dread_t ReadFileCB, Model(_unused const std::string &data, _unused m3dread_t ReadFileCB,
_unused m3dfree_t FreeCB, _unused M3D::Model mtllib) { _unused m3dfree_t FreeCB, _unused M3D::Model mtllib) {

10
src/external/qoa.h vendored
View File

@ -387,7 +387,7 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned
int slice_start = sample_index * channels + c; int slice_start = sample_index * channels + c;
int slice_end = (sample_index + slice_len) * channels + c; int slice_end = (sample_index + slice_len) * channels + c;
/* Brute for search for the best scalefactor. Just go through all /* Brute force search for the best scalefactor. Just go through all
16 scalefactors, encode all samples for the current slice and 16 scalefactors, encode all samples for the current slice and
meassure the total squared error. */ meassure the total squared error. */
qoa_uint64_t best_rank = -1; qoa_uint64_t best_rank = -1;
@ -402,7 +402,7 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned
/* There is a strong correlation between the scalefactors of /* There is a strong correlation between the scalefactors of
neighboring slices. As an optimization, start testing neighboring slices. As an optimization, start testing
the best scalefactor of the previous slice first. */ the best scalefactor of the previous slice first. */
int scalefactor = (sfi + prev_scalefactor[c]) % 16; int scalefactor = (sfi + prev_scalefactor[c]) & (16 - 1);
/* We have to reset the LMS state to the last known good one /* We have to reset the LMS state to the last known good one
before trying each scalefactor, as each pass updates the LMS before trying each scalefactor, as each pass updates the LMS
@ -500,7 +500,7 @@ void *qoa_encode(const short *sample_data, qoa_desc *qoa, unsigned int *out_len)
num_frames * QOA_LMS_LEN * 4 * qoa->channels + /* 4 * 4 bytes lms state per channel */ num_frames * QOA_LMS_LEN * 4 * qoa->channels + /* 4 * 4 bytes lms state per channel */
num_slices * 8 * qoa->channels; /* 8 byte slices */ num_slices * 8 * qoa->channels; /* 8 byte slices */
unsigned char *bytes = (unsigned char *)QOA_MALLOC(encoded_size); unsigned char *bytes = QOA_MALLOC(encoded_size);
for (unsigned int c = 0; c < qoa->channels; c++) { for (unsigned int c = 0; c < qoa->channels; c++) {
/* Set the initial LMS weights to {0, 0, -1, 2}. This helps with the /* Set the initial LMS weights to {0, 0, -1, 2}. This helps with the
@ -657,7 +657,7 @@ short *qoa_decode(const unsigned char *bytes, int size, qoa_desc *qoa) {
/* Calculate the required size of the sample buffer and allocate */ /* Calculate the required size of the sample buffer and allocate */
int total_samples = qoa->samples * qoa->channels; int total_samples = qoa->samples * qoa->channels;
short *sample_data = (short *)QOA_MALLOC(total_samples * sizeof(short)); short *sample_data = QOA_MALLOC(total_samples * sizeof(short));
unsigned int sample_index = 0; unsigned int sample_index = 0;
unsigned int frame_len; unsigned int frame_len;
@ -733,7 +733,7 @@ void *qoa_read(const char *filename, qoa_desc *qoa) {
bytes_read = fread(data, 1, size, f); bytes_read = fread(data, 1, size, f);
fclose(f); fclose(f);
sample_data = qoa_decode((const unsigned char *)data, bytes_read, qoa); sample_data = qoa_decode(data, bytes_read, qoa);
QOA_FREE(data); QOA_FREE(data);
return sample_data; return sample_data;
} }

4
src/external/qoi.h vendored
View File

@ -427,7 +427,7 @@ void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) {
run = 0; run = 0;
} }
index_pos = QOI_COLOR_HASH(px) % 64; index_pos = QOI_COLOR_HASH(px) & (64 - 1);
if (index[index_pos].v == px.v) { if (index[index_pos].v == px.v) {
bytes[p++] = QOI_OP_INDEX | index_pos; bytes[p++] = QOI_OP_INDEX | index_pos;
@ -574,7 +574,7 @@ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels) {
run = (b1 & 0x3f); run = (b1 & 0x3f);
} }
index[QOI_COLOR_HASH(px) % 64] = px; index[QOI_COLOR_HASH(px) & (64 - 1)] = px;
} }
pixels[px_pos + 0] = px.rgba.r; pixels[px_pos + 0] = px.rgba.r;

File diff suppressed because it is too large Load Diff

View File

@ -821,6 +821,11 @@ void SetWindowSize(int width, int height)
CORE.Window.screen.height = height; CORE.Window.screen.height = height;
} }
if (!CORE.Window.usingFbo)
{
SetupViewport(CORE.Window.screen.width, CORE.Window.screen.height);
}
RGFW_window_resize(platform.window, CORE.Window.screen.width, CORE.Window.screen.height); RGFW_window_resize(platform.window, CORE.Window.screen.width, CORE.Window.screen.height);
} }

View File

@ -963,20 +963,18 @@ Sound LoadSoundFromWave(Wave wave)
if (frameCount == 0) TRACELOG(LOG_WARNING, "SOUND: Failed to get frame count for format conversion"); if (frameCount == 0) TRACELOG(LOG_WARNING, "SOUND: Failed to get frame count for format conversion");
AudioBuffer *audioBuffer = LoadAudioBuffer(AUDIO_DEVICE_FORMAT, AUDIO_DEVICE_CHANNELS, AUDIO.System.device.sampleRate, frameCount, AUDIO_BUFFER_USAGE_STATIC); AudioBuffer *audioBuffer = LoadAudioBuffer(AUDIO_DEVICE_FORMAT, AUDIO_DEVICE_CHANNELS, AUDIO.System.device.sampleRate, frameCount, AUDIO_BUFFER_USAGE_STATIC);
if (audioBuffer == NULL) if (audioBuffer != NULL)
{ {
TRACELOG(LOG_WARNING, "SOUND: Failed to create buffer"); frameCount = (ma_uint32)ma_convert_frames(audioBuffer->data, frameCount, AUDIO_DEVICE_FORMAT, AUDIO_DEVICE_CHANNELS, AUDIO.System.device.sampleRate, wave.data, frameCountIn, formatIn, wave.channels, wave.sampleRate);
return sound; // early return to avoid dereferencing the audioBuffer null pointer if (frameCount == 0) TRACELOG(LOG_WARNING, "SOUND: Failed format conversion");
sound.frameCount = frameCount;
sound.stream.sampleRate = AUDIO.System.device.sampleRate;
sound.stream.sampleSize = 32;
sound.stream.channels = AUDIO_DEVICE_CHANNELS;
sound.stream.buffer = audioBuffer;
} }
else TRACELOG(LOG_WARNING, "SOUND: Failed to create buffer");
frameCount = (ma_uint32)ma_convert_frames(audioBuffer->data, frameCount, AUDIO_DEVICE_FORMAT, AUDIO_DEVICE_CHANNELS, AUDIO.System.device.sampleRate, wave.data, frameCountIn, formatIn, wave.channels, wave.sampleRate);
if (frameCount == 0) TRACELOG(LOG_WARNING, "SOUND: Failed format conversion");
sound.frameCount = frameCount;
sound.stream.sampleRate = AUDIO.System.device.sampleRate;
sound.stream.sampleSize = 32;
sound.stream.channels = AUDIO_DEVICE_CHANNELS;
sound.stream.buffer = audioBuffer;
} }
return sound; return sound;
@ -992,25 +990,23 @@ Sound LoadSoundAlias(Sound source)
{ {
AudioBuffer *audioBuffer = LoadAudioBuffer(AUDIO_DEVICE_FORMAT, AUDIO_DEVICE_CHANNELS, AUDIO.System.device.sampleRate, 0, AUDIO_BUFFER_USAGE_STATIC); AudioBuffer *audioBuffer = LoadAudioBuffer(AUDIO_DEVICE_FORMAT, AUDIO_DEVICE_CHANNELS, AUDIO.System.device.sampleRate, 0, AUDIO_BUFFER_USAGE_STATIC);
if (audioBuffer == NULL) if (audioBuffer != NULL)
{ {
TRACELOG(LOG_WARNING, "SOUND: Failed to create buffer"); audioBuffer->sizeInFrames = source.stream.buffer->sizeInFrames;
return sound; // Early return to avoid dereferencing the audioBuffer null pointer audioBuffer->data = source.stream.buffer->data;
// Initalize the buffer as if it was new
audioBuffer->volume = 1.0f;
audioBuffer->pitch = 1.0f;
audioBuffer->pan = 0.0f; // Center
sound.frameCount = source.frameCount;
sound.stream.sampleRate = AUDIO.System.device.sampleRate;
sound.stream.sampleSize = 32;
sound.stream.channels = AUDIO_DEVICE_CHANNELS;
sound.stream.buffer = audioBuffer;
} }
else TRACELOG(LOG_WARNING, "SOUND: Failed to create buffer");
audioBuffer->sizeInFrames = source.stream.buffer->sizeInFrames;
audioBuffer->data = source.stream.buffer->data;
// Initalize the buffer as if it was new
audioBuffer->volume = 1.0f;
audioBuffer->pitch = 1.0f;
audioBuffer->pan = 0.0f; // Center
sound.frameCount = source.frameCount;
sound.stream.sampleRate = AUDIO.System.device.sampleRate;
sound.stream.sampleSize = 32;
sound.stream.channels = AUDIO_DEVICE_CHANNELS;
sound.stream.buffer = audioBuffer;
} }
return sound; return sound;
@ -1071,7 +1067,7 @@ void UpdateSound(Sound sound, const void *data, int frameCount)
// Export wave data to file // Export wave data to file
bool ExportWave(Wave wave, const char *fileName) bool ExportWave(Wave wave, const char *fileName)
{ {
bool success = false; bool result = false;
if (false) { } if (false) { }
#if SUPPORT_FILEFORMAT_WAV #if SUPPORT_FILEFORMAT_WAV
@ -1088,11 +1084,11 @@ bool ExportWave(Wave wave, const char *fileName)
void *fileData = NULL; void *fileData = NULL;
size_t fileDataSize = 0; size_t fileDataSize = 0;
success = drwav_init_memory_write(&wav, &fileData, &fileDataSize, &format, NULL); result = drwav_init_memory_write(&wav, &fileData, &fileDataSize, &format, NULL);
if (success) success = (int)drwav_write_pcm_frames(&wav, wave.frameCount, wave.data); if (result) result = (int)drwav_write_pcm_frames(&wav, wave.frameCount, wave.data);
drwav_result result = drwav_uninit(&wav); drwav_result result = drwav_uninit(&wav);
if (result == DRWAV_SUCCESS) success = SaveFileData(fileName, (unsigned char *)fileData, (unsigned int)fileDataSize); if (result == DRWAV_SUCCESS) result = SaveFileData(fileName, (unsigned char *)fileData, (unsigned int)fileDataSize);
drwav_free(fileData, NULL); drwav_free(fileData, NULL);
} }
@ -1108,7 +1104,7 @@ bool ExportWave(Wave wave, const char *fileName)
qoa.samples = wave.frameCount; qoa.samples = wave.frameCount;
int bytesWritten = qoa_write(fileName, (const short *)wave.data, &qoa); int bytesWritten = qoa_write(fileName, (const short *)wave.data, &qoa);
if (bytesWritten > 0) success = true; if (bytesWritten > 0) result = true;
} }
else TRACELOG(LOG_WARNING, "AUDIO: Wave data must be 16 bit per sample for QOA format export"); else TRACELOG(LOG_WARNING, "AUDIO: Wave data must be 16 bit per sample for QOA format export");
} }
@ -1117,19 +1113,19 @@ bool ExportWave(Wave wave, const char *fileName)
{ {
// Export raw sample data (without header) // Export raw sample data (without header)
// NOTE: It's up to the user to track wave parameters // NOTE: It's up to the user to track wave parameters
success = SaveFileData(fileName, wave.data, wave.frameCount*wave.channels*wave.sampleSize/8); result = SaveFileData(fileName, wave.data, wave.frameCount*wave.channels*wave.sampleSize/8);
} }
if (success) TRACELOG(LOG_INFO, "FILEIO: [%s] Wave data exported successfully", fileName); if (result) TRACELOG(LOG_INFO, "FILEIO: [%s] Wave data exported successfully", fileName);
else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export wave data", fileName); else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export wave data", fileName);
return success; return result;
} }
// Export wave sample data to code (.h) // Export wave sample data to code (.h)
bool ExportWaveAsCode(Wave wave, const char *fileName) bool ExportWaveAsCode(Wave wave, const char *fileName)
{ {
bool success = false; bool result = false;
#ifndef TEXT_BYTES_PER_LINE #ifndef TEXT_BYTES_PER_LINE
#define TEXT_BYTES_PER_LINE 20 #define TEXT_BYTES_PER_LINE 20
@ -1183,14 +1179,14 @@ bool ExportWaveAsCode(Wave wave, const char *fileName)
} }
// NOTE: Text data length exported is determined by '\0' (NULL) character // NOTE: Text data length exported is determined by '\0' (NULL) character
success = SaveFileText(fileName, txtData); result = SaveFileText(fileName, txtData);
RL_FREE(txtData); RL_FREE(txtData);
if (success != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Wave as code exported successfully", fileName); if (result != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Wave as code exported successfully", fileName);
else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export wave as code", fileName); else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export wave as code", fileName);
return success; return result;
} }
// Play a sound // Play a sound
@ -2187,12 +2183,15 @@ void UpdateAudioStream(AudioStream stream, const void *data, int frameCount)
// Check if any audio stream buffers requires refill // Check if any audio stream buffers requires refill
bool IsAudioStreamProcessed(AudioStream stream) bool IsAudioStreamProcessed(AudioStream stream)
{ {
if (stream.buffer == NULL) return false;
bool result = false; bool result = false;
ma_mutex_lock(&AUDIO.System.lock);
result = stream.buffer->isSubBufferProcessed[0] || stream.buffer->isSubBufferProcessed[1]; if (stream.buffer != NULL)
ma_mutex_unlock(&AUDIO.System.lock); {
ma_mutex_lock(&AUDIO.System.lock);
result = stream.buffer->isSubBufferProcessed[0] || stream.buffer->isSubBufferProcessed[1];
ma_mutex_unlock(&AUDIO.System.lock);
}
return result; return result;
} }
@ -2885,6 +2884,8 @@ static unsigned char *LoadFileData(const char *fileName, int *dataSize)
// Save data to file from buffer // Save data to file from buffer
static bool SaveFileData(const char *fileName, void *data, int dataSize) static bool SaveFileData(const char *fileName, void *data, int dataSize)
{ {
bool result = true;
if (fileName != NULL) if (fileName != NULL)
{ {
FILE *file = fopen(fileName, "wb"); FILE *file = fopen(fileName, "wb");
@ -2902,21 +2903,23 @@ static bool SaveFileData(const char *fileName, void *data, int dataSize)
else else
{ {
TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open file", fileName); TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open file", fileName);
return false; result = false;
} }
} }
else else
{ {
TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid"); TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid");
return false; result = false;
} }
return true; return result;
} }
// Save text data to file (write), string must be '\0' terminated // Save text data to file (write), string must be '\0' terminated
static bool SaveFileText(const char *fileName, char *text) static bool SaveFileText(const char *fileName, char *text)
{ {
bool result = true;
if (fileName != NULL) if (fileName != NULL)
{ {
FILE *file = fopen(fileName, "wt"); FILE *file = fopen(fileName, "wt");
@ -2934,16 +2937,16 @@ static bool SaveFileText(const char *fileName, char *text)
else else
{ {
TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open text file", fileName); TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open text file", fileName);
return false; result = false;
} }
} }
else else
{ {
TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid"); TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid");
return false; result = false;
} }
return true; return result;
} }
#endif #endif

View File

@ -1161,8 +1161,8 @@ RLAPI int MakeDirectory(const char *dirPath); // Create di
RLAPI bool ChangeDirectory(const char *dirPath); // Change working directory, return true on success RLAPI bool ChangeDirectory(const char *dirPath); // Change working directory, return true on success
RLAPI bool IsPathFile(const char *path); // Check if a given path is a file or a directory RLAPI bool IsPathFile(const char *path); // Check if a given path is a file or a directory
RLAPI bool IsFileNameValid(const char *fileName); // Check if fileName is valid for the platform/OS RLAPI bool IsFileNameValid(const char *fileName); // Check if fileName is valid for the platform/OS
RLAPI FilePathList LoadDirectoryFiles(const char *dirPath); // Load directory filepaths RLAPI FilePathList LoadDirectoryFiles(const char *dirPath); // Load directory filepaths, files and directories, no subdirs scan
RLAPI FilePathList LoadDirectoryFilesEx(const char *basePath, const char *filter, bool scanSubdirs); // Load directory filepaths with extension filtering and recursive directory scan. Use 'DIR' in the filter string to include directories in the result RLAPI FilePathList LoadDirectoryFilesEx(const char *basePath, const char *filter, bool scanSubdirs); // Load directory filepaths with extension filtering and subdir scan; some filters available: "*.*", "FILES*", "DIRS*"
RLAPI void UnloadDirectoryFiles(FilePathList files); // Unload filepaths RLAPI void UnloadDirectoryFiles(FilePathList files); // Unload filepaths
RLAPI bool IsFileDropped(void); // Check if a file has been dropped into window RLAPI bool IsFileDropped(void); // Check if a file has been dropped into window
RLAPI FilePathList LoadDroppedFiles(void); // Load dropped filepaths RLAPI FilePathList LoadDroppedFiles(void); // Load dropped filepaths
@ -1359,7 +1359,7 @@ RLAPI Image LoadImageFromScreen(void);
RLAPI bool IsImageValid(Image image); // Check if an image is valid (data and parameters) RLAPI bool IsImageValid(Image image); // Check if an image is valid (data and parameters)
RLAPI void UnloadImage(Image image); // Unload image from CPU memory (RAM) RLAPI void UnloadImage(Image image); // Unload image from CPU memory (RAM)
RLAPI bool ExportImage(Image image, const char *fileName); // Export image data to file, returns true on success RLAPI bool ExportImage(Image image, const char *fileName); // Export image data to file, returns true on success
RLAPI unsigned char *ExportImageToMemory(Image image, const char *fileType, int *fileSize); // Export image to memory buffer RLAPI unsigned char *ExportImageToMemory(Image image, const char *fileType, int *fileSize); // Export image to memory buffer, memory must be MemFree()
RLAPI bool ExportImageAsCode(Image image, const char *fileName); // Export image as code file defining an array of bytes, returns true on success RLAPI bool ExportImageAsCode(Image image, const char *fileName); // Export image as code file defining an array of bytes, returns true on success
// Image generation functions // Image generation functions
@ -1528,7 +1528,7 @@ RLAPI const char *CodepointToUTF8(int codepoint, int *utf8Size);
// Text strings management functions (no UTF-8 strings, only byte chars) // Text strings management functions (no UTF-8 strings, only byte chars)
// WARNING 1: Most of these functions use internal static buffers[], it's recommended to store returned data on user-side for re-use // WARNING 1: Most of these functions use internal static buffers[], it's recommended to store returned data on user-side for re-use
// WARNING 2: Some strings allocate memory internally for the returned strings, those strings must be free by user using MemFree() // WARNING 2: Some functions allocate memory internally for the returned strings, those strings must be freed by user using MemFree()
RLAPI char **LoadTextLines(const char *text, int *count); // Load text as separate lines ('\n') RLAPI char **LoadTextLines(const char *text, int *count); // Load text as separate lines ('\n')
RLAPI void UnloadTextLines(char **text, int lineCount); // Unload text lines RLAPI void UnloadTextLines(char **text, int lineCount); // Unload text lines
RLAPI int TextCopy(char *dst, const char *src); // Copy one string to another, returns bytes copied RLAPI int TextCopy(char *dst, const char *src); // Copy one string to another, returns bytes copied
@ -1538,9 +1538,12 @@ RLAPI const char *TextFormat(const char *text, ...);
RLAPI const char *TextSubtext(const char *text, int position, int length); // Get a piece of a text string RLAPI const char *TextSubtext(const char *text, int position, int length); // Get a piece of a text string
RLAPI const char *TextRemoveSpaces(const char *text); // Remove text spaces, concat words RLAPI const char *TextRemoveSpaces(const char *text); // Remove text spaces, concat words
RLAPI char *GetTextBetween(const char *text, const char *begin, const char *end); // Get text between two strings RLAPI char *GetTextBetween(const char *text, const char *begin, const char *end); // Get text between two strings
RLAPI char *TextReplace(const char *text, const char *search, const char *replacement); // Replace text string (WARNING: memory must be freed!) RLAPI char *TextReplace(const char *text, const char *search, const char *replacement); // Replace text string with new string
RLAPI char *TextReplaceBetween(const char *text, const char *begin, const char *end, const char *replacement); // Replace text between two specific strings (WARNING: memory must be freed!) RLAPI char *TextReplaceAlloc(const char *text, const char *search, const char *replacement); // Replace text string with new string, memory must be MemFree()
RLAPI char *TextInsert(const char *text, const char *insert, int position); // Insert text in a position (WARNING: memory must be freed!) RLAPI char *TextReplaceBetween(const char *text, const char *begin, const char *end, const char *replacement); // Replace text between two specific strings
RLAPI char *TextReplaceBetweenAlloc(const char *text, const char *begin, const char *end, const char *replacement); // Replace text between two specific strings, memory must be MemFree()
RLAPI char *TextInsert(const char *text, const char *insert, int position); // Insert text in a defined byte position
RLAPI char *TextInsertAlloc(const char *text, const char *insert, int position); // Insert text in a defined byte position, memory must be MemFree()
RLAPI char *TextJoin(char **textList, int count, const char *delimiter); // Join text strings with delimiter RLAPI char *TextJoin(char **textList, int count, const char *delimiter); // Join text strings with delimiter
RLAPI char **TextSplit(const char *text, char delimiter, int *count); // Split text into multiple strings, using MAX_TEXTSPLIT_COUNT static strings RLAPI char **TextSplit(const char *text, char delimiter, int *count); // Split text into multiple strings, using MAX_TEXTSPLIT_COUNT static strings
RLAPI void TextAppend(char *text, const char *append, int *position); // Append text at specific position and move cursor RLAPI void TextAppend(char *text, const char *append, int *position); // Append text at specific position and move cursor
@ -1596,8 +1599,6 @@ RLAPI void DrawModel(Model model, Vector3 position, float scale, Color tint);
RLAPI void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model with extended parameters RLAPI void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model with extended parameters
RLAPI void DrawModelWires(Model model, Vector3 position, float scale, Color tint); // Draw a model wires (with texture if set) RLAPI void DrawModelWires(Model model, Vector3 position, float scale, Color tint); // Draw a model wires (with texture if set)
RLAPI void DrawModelWiresEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model wires (with texture if set) with extended parameters RLAPI void DrawModelWiresEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model wires (with texture if set) with extended parameters
RLAPI void DrawModelPoints(Model model, Vector3 position, float scale, Color tint); // Draw a model as points
RLAPI void DrawModelPointsEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model as points with extended parameters
RLAPI void DrawBoundingBox(BoundingBox box, Color color); // Draw bounding box (wires) RLAPI void DrawBoundingBox(BoundingBox box, Color color); // Draw bounding box (wires)
RLAPI void DrawBillboard(Camera camera, Texture2D texture, Vector3 position, float scale, Color tint); // Draw a billboard texture RLAPI void DrawBillboard(Camera camera, Texture2D texture, Vector3 position, float scale, Color tint); // Draw a billboard texture
RLAPI void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle source, Vector3 position, Vector2 size, Color tint); // Draw a billboard texture defined by source RLAPI void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle source, Vector3 position, Vector2 size, Color tint); // Draw a billboard texture defined by source

View File

@ -267,15 +267,18 @@
#define MAX_AUTOMATION_EVENTS 16384 // Maximum number of automation events to record #define MAX_AUTOMATION_EVENTS 16384 // Maximum number of automation events to record
#endif #endif
// File and directory scan filters
// NOTE: Used in ScanDirectoryFiles(), LoadDirectoryFilesEx() and GetDirectoryFileCountEx()
// WARNING: Custom file filters can be specified but following raylib IsFileExtension() convention: ".png;.wav;.glb"
#ifndef FILE_FILTER_TAG_ALL #ifndef FILE_FILTER_TAG_ALL
#define FILE_FILTER_TAG_ALL "*.*" // Filter to include all file types and directories on directory scan #define FILE_FILTER_TAG_ALL "*.*" // Filter to include all file types and directories on scan
#endif // NOTE: Used in ScanDirectoryFiles(), LoadDirectoryFilesEx() and GetDirectoryFileCountEx() #endif
#ifndef FILE_FILTER_TAG_FILE_ONLY #ifndef FILE_FILTER_TAG_FILE_ONLY
#define FILE_FILTER_TAG_FILE_ONLY "FILES*" // Filter to include all file types on directory scan #define FILE_FILTER_TAG_FILE_ONLY "FILES*" // Filter to include all file types on scan (no directories)
#endif // NOTE: Used in ScanDirectoryFiles(), LoadDirectoryFilesEx() and GetDirectoryFileCountEx() #endif
#ifndef FILE_FILTER_TAG_DIR_ONLY #ifndef FILE_FILTER_TAG_DIR_ONLY
#define FILE_FILTER_TAG_DIR_ONLY "DIR*" // Filter to include directories on directory scan #define FILE_FILTER_TAG_DIR_ONLY "DIRS*" // Filter to include only directories on scan
#endif // NOTE: Used in ScanDirectoryFiles(), LoadDirectoryFilesEx() and GetDirectoryFileCountEx() #endif
// Flags bitwise operation macros // Flags bitwise operation macros
#define FLAG_SET(n, f) ((n) |= (f)) #define FLAG_SET(n, f) ((n) |= (f))
@ -821,7 +824,7 @@ int GetRenderWidth(void)
{ {
int width = 0; int width = 0;
if (CORE.Window.usingFbo) return CORE.Window.currentFbo.width; if (CORE.Window.usingFbo) width = CORE.Window.currentFbo.width;
else width = CORE.Window.render.width; else width = CORE.Window.render.width;
return width; return width;
@ -832,7 +835,7 @@ int GetRenderHeight(void)
{ {
int height = 0; int height = 0;
if (CORE.Window.usingFbo) return CORE.Window.currentFbo.height; if (CORE.Window.usingFbo) height = CORE.Window.currentFbo.height;
else height = CORE.Window.render.height; else height = CORE.Window.render.height;
return height; return height;
@ -1227,7 +1230,7 @@ void UnloadVrStereoConfig(VrStereoConfig config)
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Load shader from files and bind default locations // Load shader from files and bind default locations
// NOTE: If shader string is NULL, using default vertex/fragment shaders // NOTE: If shader filename is NULL, using default vertex/fragment shaders
Shader LoadShader(const char *vsFileName, const char *fsFileName) Shader LoadShader(const char *vsFileName, const char *fsFileName)
{ {
Shader shader = { 0 }; Shader shader = { 0 };
@ -1621,18 +1624,20 @@ int GetFPS(void)
for (int i = 0; i < FPS_CAPTURE_FRAMES_COUNT; i++) history[i] = 0; for (int i = 0; i < FPS_CAPTURE_FRAMES_COUNT; i++) history[i] = 0;
} }
if (fpsFrame == 0) return 0; if (fpsFrame != 0)
if ((GetTime() - last) > FPS_STEP)
{ {
last = (float)GetTime(); if ((GetTime() - last) > FPS_STEP)
index = (index + 1)%FPS_CAPTURE_FRAMES_COUNT; {
average -= history[index]; last = (float)GetTime();
history[index] = fpsFrame/FPS_CAPTURE_FRAMES_COUNT; index = (index + 1)%FPS_CAPTURE_FRAMES_COUNT;
average += history[index]; average -= history[index];
} history[index] = fpsFrame/FPS_CAPTURE_FRAMES_COUNT;
average += history[index];
}
fps = (int)roundf(1.0f/average); fps = (int)roundf(1.0f/average);
}
else fps = 0;
#endif #endif
return fps; return fps;
@ -2025,7 +2030,7 @@ void UnloadFileData(unsigned char *data)
// Save data to file from buffer // Save data to file from buffer
bool SaveFileData(const char *fileName, void *data, int dataSize) bool SaveFileData(const char *fileName, void *data, int dataSize)
{ {
bool success = false; bool result = false;
if (fileName != NULL) if (fileName != NULL)
{ {
@ -2043,20 +2048,20 @@ bool SaveFileData(const char *fileName, void *data, int dataSize)
else if (count != dataSize) TRACELOG(LOG_WARNING, "FILEIO: [%s] File partially written", fileName); else if (count != dataSize) TRACELOG(LOG_WARNING, "FILEIO: [%s] File partially written", fileName);
else TRACELOG(LOG_INFO, "FILEIO: [%s] File saved successfully", fileName); else TRACELOG(LOG_INFO, "FILEIO: [%s] File saved successfully", fileName);
int result = fclose(file); int closed = fclose(file);
if (result == 0) success = true; if (closed == 0) result = true;
} }
else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open file", fileName); else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open file", fileName);
} }
else TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid"); else TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid");
return success; return result;
} }
// Export data to code (.h), returns true on success // Export data to code (.h), returns true on success
bool ExportDataAsCode(const unsigned char *data, int dataSize, const char *fileName) bool ExportDataAsCode(const unsigned char *data, int dataSize, const char *fileName)
{ {
bool success = false; bool result = false;
#ifndef TEXT_BYTES_PER_LINE #ifndef TEXT_BYTES_PER_LINE
#define TEXT_BYTES_PER_LINE 20 #define TEXT_BYTES_PER_LINE 20
@ -2096,14 +2101,14 @@ bool ExportDataAsCode(const unsigned char *data, int dataSize, const char *fileN
byteCount += sprintf(txtData + byteCount, "0x%x };\n", data[dataSize - 1]); byteCount += sprintf(txtData + byteCount, "0x%x };\n", data[dataSize - 1]);
// NOTE: Text data size exported is determined by '\0' (NULL) character // NOTE: Text data size exported is determined by '\0' (NULL) character
success = SaveFileText(fileName, txtData); result = SaveFileText(fileName, txtData);
RL_FREE(txtData); RL_FREE(txtData);
if (success != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Data as code exported successfully", fileName); if (result != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Data as code exported successfully", fileName);
else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export data as code", fileName); else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export data as code", fileName);
return success; return result;
} }
// Load text data from file, returns a '\0' terminated string // Load text data from file, returns a '\0' terminated string
@ -2166,7 +2171,7 @@ void UnloadFileText(char *text)
// Save text data to file (write), string must be '\0' terminated // Save text data to file (write), string must be '\0' terminated
bool SaveFileText(const char *fileName, const char *text) bool SaveFileText(const char *fileName, const char *text)
{ {
bool success = false; bool result = false;
if (fileName != NULL) if (fileName != NULL)
{ {
@ -2181,14 +2186,14 @@ bool SaveFileText(const char *fileName, const char *text)
if (count < 0) TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to write text file", fileName); if (count < 0) TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to write text file", fileName);
else TRACELOG(LOG_INFO, "FILEIO: [%s] Text file saved successfully", fileName); else TRACELOG(LOG_INFO, "FILEIO: [%s] Text file saved successfully", fileName);
int result = fclose(file); int closed = fclose(file);
if (result == 0) success = true; if (closed == 0) result = true;
} }
else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open text file", fileName); else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open text file", fileName);
} }
else TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid"); else TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid");
return success; return result;
} }
// File access custom callbacks // File access custom callbacks
@ -2298,7 +2303,7 @@ int FileTextReplace(const char *fileName, const char *search, const char *replac
if (FileExists(fileName)) if (FileExists(fileName))
{ {
fileText = LoadFileText(fileName); fileText = LoadFileText(fileName);
fileTextUpdated = TextReplace(fileText, search, replacement); fileTextUpdated = TextReplaceAlloc(fileText, search, replacement);
result = SaveFileText(fileName, fileTextUpdated); result = SaveFileText(fileName, fileTextUpdated);
MemFree(fileTextUpdated); MemFree(fileTextUpdated);
UnloadFileText(fileText); UnloadFileText(fileText);
@ -2718,17 +2723,18 @@ const char *GetApplicationDirectory(void)
// Load directory filepaths // Load directory filepaths
// NOTE: Base path is prepended to the scanned filepaths // NOTE: Base path is prepended to the scanned filepaths
// WARNING: Directory is scanned twice, first time to get files count // WARNING: Directory is scanned twice, first time to get paths count
// No recursive scanning is done! // Scanneed files and directories, no recursive/subdirs scanning
FilePathList LoadDirectoryFiles(const char *dirPath) FilePathList LoadDirectoryFiles(const char *dirPath)
{ {
return LoadDirectoryFilesEx(dirPath, FILE_FILTER_TAG_ALL, false); return LoadDirectoryFilesEx(dirPath, FILE_FILTER_TAG_ALL, false);
} }
// Load directory filepaths with extension filtering and recursive directory scan // Load directory filepaths with extension filtering and recursive directory scan
// Use 'DIR*' to include directories on directory scan // Use "*.*" to include all files and directories on scan
// Use '*.*' to include all file types and directories on directory scan // Use "FILES*" to include only files on scan
// WARNING: Directory is scanned twice, first time to get files count // Use "DIRS*" to include only directories on scan
// WARNING: Directory is scanned twice, first time to get paths count
FilePathList LoadDirectoryFilesEx(const char *basePath, const char *filter, bool scanSubdirs) FilePathList LoadDirectoryFilesEx(const char *basePath, const char *filter, bool scanSubdirs)
{ {
FilePathList files = { 0 }; FilePathList files = { 0 };
@ -3628,7 +3634,7 @@ void UnloadAutomationEventList(AutomationEventList list)
// Export automation events list as text file // Export automation events list as text file
bool ExportAutomationEventList(AutomationEventList list, const char *fileName) bool ExportAutomationEventList(AutomationEventList list, const char *fileName)
{ {
bool success = false; bool result = false;
#if SUPPORT_AUTOMATION_EVENTS #if SUPPORT_AUTOMATION_EVENTS
// Export events as binary file // Export events as binary file
@ -3646,7 +3652,7 @@ bool ExportAutomationEventList(AutomationEventList list, const char *fileName)
memcpy(binBuffer + offset, list.events, sizeof(AutomationEvent)*list.count); memcpy(binBuffer + offset, list.events, sizeof(AutomationEvent)*list.count);
offset += sizeof(AutomationEvent)*list.count; offset += sizeof(AutomationEvent)*list.count;
success = SaveFileData(TextFormat("%s.rae",fileName), binBuffer, binarySize); result = SaveFileData(TextFormat("%s.rae",fileName), binBuffer, binarySize);
RL_FREE(binBuffer); RL_FREE(binBuffer);
} }
*/ */
@ -3677,12 +3683,12 @@ bool ExportAutomationEventList(AutomationEventList list, const char *fileName)
} }
// NOTE: Text data size exported is determined by '\0' (NULL) character // NOTE: Text data size exported is determined by '\0' (NULL) character
success = SaveFileText(fileName, txtData); result = SaveFileText(fileName, txtData);
RL_FREE(txtData); RL_FREE(txtData);
#endif #endif
return success; return result;
} }
// Setup automation event list to record to // Setup automation event list to record to

View File

@ -1613,6 +1613,11 @@ void rlNormal3f(float x, float y, float z)
normaly = RLGL.State.transform.m1*x + RLGL.State.transform.m5*y + RLGL.State.transform.m9*z; normaly = RLGL.State.transform.m1*x + RLGL.State.transform.m5*y + RLGL.State.transform.m9*z;
normalz = RLGL.State.transform.m2*x + RLGL.State.transform.m6*y + RLGL.State.transform.m10*z; normalz = RLGL.State.transform.m2*x + RLGL.State.transform.m6*y + RLGL.State.transform.m10*z;
} }
// NOTE: Default behavior assumes the normal vector is in the correct space for what the shader expects,
// it could be not normalized to 0.0f..1.0f, magnitud can be useed for some effects
/*
// WARNING: Vector normalization if required
float length = sqrtf(normalx*normalx + normaly*normaly + normalz*normalz); float length = sqrtf(normalx*normalx + normaly*normaly + normalz*normalz);
if (length != 0.0f) if (length != 0.0f)
{ {
@ -1621,6 +1626,7 @@ void rlNormal3f(float x, float y, float z)
normaly *= ilength; normaly *= ilength;
normalz *= ilength; normalz *= ilength;
} }
*/
RLGL.State.normalx = normalx; RLGL.State.normalx = normalx;
RLGL.State.normaly = normaly; RLGL.State.normaly = normaly;
RLGL.State.normalz = normalz; RLGL.State.normalz = normalz;
@ -4647,14 +4653,14 @@ void rlUpdateShaderBuffer(unsigned int id, const void *data, unsigned int dataSi
// Get SSBO buffer size // Get SSBO buffer size
unsigned int rlGetShaderBufferSize(unsigned int id) unsigned int rlGetShaderBufferSize(unsigned int id)
{ {
unsigned int result = 0;
#if defined(GRAPHICS_API_OPENGL_43) #if defined(GRAPHICS_API_OPENGL_43)
GLint64 size = 0; GLint64 size = 0;
glBindBuffer(GL_SHADER_STORAGE_BUFFER, id); glBindBuffer(GL_SHADER_STORAGE_BUFFER, id);
glGetBufferParameteri64v(GL_SHADER_STORAGE_BUFFER, GL_BUFFER_SIZE, &size); glGetBufferParameteri64v(GL_SHADER_STORAGE_BUFFER, GL_BUFFER_SIZE, &size);
return (size > 0)? (unsigned int)size : 0; if (size > 0) result = (unsigned int)size;
#else
return 0;
#endif #endif
return result;
} }
// Read SSBO buffer data (GPU->CPU) // Read SSBO buffer data (GPU->CPU)

View File

@ -10,6 +10,7 @@
* #define SUPPORT_FILEFORMAT_MTL * #define SUPPORT_FILEFORMAT_MTL
* #define SUPPORT_FILEFORMAT_IQM * #define SUPPORT_FILEFORMAT_IQM
* #define SUPPORT_FILEFORMAT_GLTF * #define SUPPORT_FILEFORMAT_GLTF
* #define SUPPORT_FILEFORMAT_GLTF_WRITE
* #define SUPPORT_FILEFORMAT_VOX * #define SUPPORT_FILEFORMAT_VOX
* #define SUPPORT_FILEFORMAT_M3D * #define SUPPORT_FILEFORMAT_M3D
* Selected desired fileformats to be supported for model data loading * Selected desired fileformats to be supported for model data loading
@ -71,6 +72,11 @@
#define CGLTF_IMPLEMENTATION #define CGLTF_IMPLEMENTATION
#include "external/cgltf.h" // glTF file format loading #include "external/cgltf.h" // glTF file format loading
#endif #endif
#if SUPPORT_FILEFORMAT_GLTF_WRITE
// NOTE: No need for custom allocators, memory buffer provided
#define CGLTF_WRITE_IMPLEMENTATION
#include "external/cgltf_write.h" // glTF file format writing
#endif
#if SUPPORT_FILEFORMAT_VOX #if SUPPORT_FILEFORMAT_VOX
#define VOX_MALLOC RL_MALLOC #define VOX_MALLOC RL_MALLOC
@ -1281,7 +1287,6 @@ void UploadMesh(Mesh *mesh, bool dynamic)
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
mesh->vaoId = rlLoadVertexArray(); mesh->vaoId = rlLoadVertexArray();
if (mesh->vaoId == 0) return;
rlEnableVertexArray(mesh->vaoId); rlEnableVertexArray(mesh->vaoId);
@ -1950,7 +1955,7 @@ void UnloadMesh(Mesh mesh)
// Export mesh data to file // Export mesh data to file
bool ExportMesh(Mesh mesh, const char *fileName) bool ExportMesh(Mesh mesh, const char *fileName)
{ {
bool success = false; bool result = false;
if (IsFileExtension(fileName, ".obj")) if (IsFileExtension(fileName, ".obj"))
{ {
@ -2014,22 +2019,35 @@ bool ExportMesh(Mesh mesh, const char *fileName)
} }
// NOTE: Text data length exported is determined by '\0' (NULL) character // NOTE: Text data length exported is determined by '\0' (NULL) character
success = SaveFileText(fileName, txtData); result = SaveFileText(fileName, txtData);
RL_FREE(txtData); RL_FREE(txtData);
} }
else if (IsFileExtension(fileName, ".gltf")) // Or .glb
{
// TODO: Implement gltf/glb support
/*
cgltf_size expected = cgltf_write(options, NULL, 0, data);
char *buffer = (char *)RL_CALLOC(expected, 0);
cgltf_size actual = cgltf_write(options, buffer, expected, data);
// NOTE: cgltf_write() includes a NULL terminator that should be ommited in case of a .glb
if (options->type == cgltf_file_type_glb) cgltf_write_glb(file, buffer, actual - 1, data->bin, data->bin_size);
else SaveFileText(fileName, buffer); // Write a plain JSON file
*/
}
else if (IsFileExtension(fileName, ".raw")) else if (IsFileExtension(fileName, ".raw"))
{ {
// TODO: Support additional file formats to export mesh vertex data // TODO: Support additional file formats to export mesh vertex data
} }
return success; return result;
} }
// Export mesh as code file (.h) defining multiple arrays of vertex attributes // Export mesh as code file (.h) defining multiple arrays of vertex attributes
bool ExportMeshAsCode(Mesh mesh, const char *fileName) bool ExportMeshAsCode(Mesh mesh, const char *fileName)
{ {
bool success = false; bool result = false;
#ifndef TEXT_BYTES_PER_LINE #ifndef TEXT_BYTES_PER_LINE
#define TEXT_BYTES_PER_LINE 20 #define TEXT_BYTES_PER_LINE 20
@ -2113,14 +2131,14 @@ bool ExportMeshAsCode(Mesh mesh, const char *fileName)
//----------------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------------
// NOTE: Text data size exported is determined by '\0' (NULL) character // NOTE: Text data size exported is determined by '\0' (NULL) character
success = SaveFileText(fileName, txtData); result = SaveFileText(fileName, txtData);
RL_FREE(txtData); RL_FREE(txtData);
//if (success != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Image as code exported successfully", fileName); //if (result != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Image as code exported successfully", fileName);
//else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export image as code", fileName); //else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export image as code", fileName);
return success; return result;
} }
#if SUPPORT_FILEFORMAT_OBJ || SUPPORT_FILEFORMAT_MTL #if SUPPORT_FILEFORMAT_OBJ || SUPPORT_FILEFORMAT_MTL
@ -3961,32 +3979,6 @@ void DrawModelWiresEx(Model model, Vector3 position, Vector3 rotationAxis, float
rlDisableWireMode(); rlDisableWireMode();
} }
// Draw a model points
// WARNING: OpenGL ES 2.0 does not support point mode drawing
void DrawModelPoints(Model model, Vector3 position, float scale, Color tint)
{
rlEnablePointMode();
rlDisableBackfaceCulling();
DrawModel(model, position, scale, tint);
rlEnableBackfaceCulling();
rlDisablePointMode();
}
// Draw a model points
// WARNING: OpenGL ES 2.0 does not support point mode drawing
void DrawModelPointsEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint)
{
rlEnablePointMode();
rlDisableBackfaceCulling();
DrawModelEx(model, position, rotationAxis, rotationAngle, scale, tint);
rlEnableBackfaceCulling();
rlDisablePointMode();
}
// Draw a billboard // Draw a billboard
void DrawBillboard(Camera camera, Texture2D texture, Vector3 position, float scale, Color tint) void DrawBillboard(Camera camera, Texture2D texture, Vector3 position, float scale, Color tint)
{ {
@ -5292,7 +5284,7 @@ static cgltf_result LoadFileGLTFCallback(const struct cgltf_memory_options *memo
} }
// Release file data callback for cgltf // Release file data callback for cgltf
static void ReleaseFileGLTFCallback(const struct cgltf_memory_options *memoryOptions, const struct cgltf_file_options *fileOptions, void *data) static void ReleaseFileGLTFCallback(const struct cgltf_memory_options *memoryOptions, const struct cgltf_file_options *fileOptions, void *data, cgltf_size size)
{ {
UnloadFileData((unsigned char *)data); UnloadFileData((unsigned char *)data);
} }

View File

@ -2348,16 +2348,18 @@ bool CheckCollisionCircleRec(Vector2 center, float radius, Rectangle rec)
float dx = fabsf(center.x - recCenterX); float dx = fabsf(center.x - recCenterX);
float dy = fabsf(center.y - recCenterY); float dy = fabsf(center.y - recCenterY);
if (dx > (rec.width/2.0f + radius)) { return false; } if ((dx <= (rec.width/2.0f + radius)) && (dy <= (rec.height/2.0f + radius)))
if (dy > (rec.height/2.0f + radius)) { return false; } {
if (dx <= (rec.width/2.0f)) collision = true;
else if (dy <= (rec.height/2.0f)) collision = true;
else
{
float cornerDistanceSq = (dx - rec.width/2.0f)*(dx - rec.width/2.0f) +
(dy - rec.height/2.0f)*(dy - rec.height/2.0f);
if (dx <= (rec.width/2.0f)) { return true; } collision = (cornerDistanceSq <= (radius*radius));
if (dy <= (rec.height/2.0f)) { return true; } }
}
float cornerDistanceSq = (dx - rec.width/2.0f)*(dx - rec.width/2.0f) +
(dy - rec.height/2.0f)*(dy - rec.height/2.0f);
collision = (cornerDistanceSq <= (radius*radius));
return collision; return collision;
} }
@ -2418,25 +2420,31 @@ bool CheckCollisionPointLine(Vector2 point, Vector2 p1, Vector2 p2, int threshol
// Check if circle collides with a line created between two points [p1] and [p2] // Check if circle collides with a line created between two points [p1] and [p2]
bool CheckCollisionCircleLine(Vector2 center, float radius, Vector2 p1, Vector2 p2) bool CheckCollisionCircleLine(Vector2 center, float radius, Vector2 p1, Vector2 p2)
{ {
bool collision = false;
float dx = p1.x - p2.x; float dx = p1.x - p2.x;
float dy = p1.y - p2.y; float dy = p1.y - p2.y;
if ((fabsf(dx) + fabsf(dy)) <= FLT_EPSILON) if ((fabsf(dx) + fabsf(dy)) <= FLT_EPSILON)
{ {
return CheckCollisionCircles(p1, 0, center, radius); collision = CheckCollisionCircles(p1, 0, center, radius);
}
else
{
float lengthSQ = ((dx*dx) + (dy*dy));
float dotProduct = (((center.x - p1.x)*(p2.x - p1.x)) + ((center.y - p1.y)*(p2.y - p1.y)))/(lengthSQ);
if (dotProduct > 1.0f) dotProduct = 1.0f;
else if (dotProduct < 0.0f) dotProduct = 0.0f;
float dx2 = (p1.x - (dotProduct*(dx))) - center.x;
float dy2 = (p1.y - (dotProduct*(dy))) - center.y;
float distanceSQ = ((dx2*dx2) + (dy2*dy2));
if (distanceSQ <= radius*radius) collision = true;
} }
float lengthSQ = ((dx*dx) + (dy*dy)); return collision;
float dotProduct = (((center.x - p1.x)*(p2.x - p1.x)) + ((center.y - p1.y)*(p2.y - p1.y)))/(lengthSQ);
if (dotProduct > 1.0f) dotProduct = 1.0f;
else if (dotProduct < 0.0f) dotProduct = 0.0f;
float dx2 = (p1.x - (dotProduct*(dx))) - center.x;
float dy2 = (p1.y - (dotProduct*(dy))) - center.y;
float distanceSQ = ((dx2*dx2) + (dy2*dy2));
return (distanceSQ <= radius*radius);
} }
// Get collision rectangle for two rectangles collision // Get collision rectangle for two rectangles collision

View File

@ -447,7 +447,7 @@ Font LoadFontFromImage(Image image, Color key, int firstChar)
int j = 0; int j = 0;
while (((lineSpacing + j) < image.height) && while (((lineSpacing + j) < image.height) &&
!COLOR_EQUAL(pixels[(lineSpacing + j)*image.width + charSpacing], key)) j++; !COLOR_EQUAL(pixels[(lineSpacing + j)*image.width + charSpacing], key)) j++;
charHeight = j; charHeight = j;
@ -1017,7 +1017,7 @@ void UnloadFont(Font font)
// Export font as code file, returns true on success // Export font as code file, returns true on success
bool ExportFontAsCode(Font font, const char *fileName) bool ExportFontAsCode(Font font, const char *fileName)
{ {
bool success = false; bool result = false;
#ifndef TEXT_BYTES_PER_LINE #ifndef TEXT_BYTES_PER_LINE
#define TEXT_BYTES_PER_LINE 20 #define TEXT_BYTES_PER_LINE 20
@ -1159,14 +1159,14 @@ bool ExportFontAsCode(Font font, const char *fileName)
UnloadImage(image); UnloadImage(image);
// NOTE: Text data size exported is determined by '\0' (NULL) character // NOTE: Text data size exported is determined by '\0' (NULL) character
success = SaveFileText(fileName, txtData); result = SaveFileText(fileName, txtData);
RL_FREE(txtData); RL_FREE(txtData);
if (success != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Font as code exported successfully", fileName); if (result != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Font as code exported successfully", fileName);
else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export font as code", fileName); else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export font as code", fileName);
return success; return result;
} }
// Draw current FPS // Draw current FPS
@ -1395,8 +1395,7 @@ Vector2 MeasureTextCodepoints(Font font, const int *codepoints, int length, floa
{ {
Vector2 textSize = { 0 }; Vector2 textSize = { 0 };
// Security check if ((font.texture.id == 0) || (codepoints == NULL) || (length == 0)) return textSize; // Security check
if ((font.texture.id == 0) || (codepoints == NULL) || (length == 0)) return textSize;
float textWidth = 0.0f; float textWidth = 0.0f;
// Used to count longer text line width // Used to count longer text line width
@ -1699,17 +1698,18 @@ const char *TextSubtext(const char *text, int position, int length)
{ {
int textLength = TextLength(text); int textLength = TextLength(text);
if (position >= textLength) return buffer; // First char is already '\0' by memset if (position < textLength)
{
int maxLength = textLength - position;
if (length > maxLength) length = maxLength;
if (length >= MAX_TEXT_BUFFER_LENGTH) length = MAX_TEXT_BUFFER_LENGTH - 1;
int maxLength = textLength - position; // NOTE: Alternative: memcpy(buffer, text + position, length)
if (length > maxLength) length = maxLength;
if (length >= MAX_TEXT_BUFFER_LENGTH) length = MAX_TEXT_BUFFER_LENGTH - 1;
// NOTE: Alternative: memcpy(buffer, text + position, length) for (int c = 0; c < length; c++) buffer[c] = text[position + c];
for (int c = 0; c < length; c++) buffer[c] = text[position + c]; buffer[length] = '\0';
}
buffer[length] = '\0';
} }
return buffer; return buffer;
@ -1736,8 +1736,6 @@ const char *TextRemoveSpaces(const char *text)
// Get text between two strings // Get text between two strings
char *GetTextBetween(const char *text, const char *begin, const char *end) char *GetTextBetween(const char *text, const char *begin, const char *end)
{ {
#define MAX_TEXT_BETWEEN_SIZE 1024
static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 }; static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH); memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH);
@ -1752,8 +1750,8 @@ char *GetTextBetween(const char *text, const char *begin, const char *end)
{ {
endIndex += (beginIndex + beginLen); endIndex += (beginIndex + beginLen);
int len = (endIndex - beginIndex - beginLen); int len = (endIndex - beginIndex - beginLen);
if (len < (MAX_TEXT_BETWEEN_SIZE - 1)) strncpy(buffer, text + beginIndex + beginLen, len); if (len < (MAX_TEXT_BUFFER_LENGTH - 1)) strncpy(buffer, text + beginIndex + beginLen, len);
else strncpy(buffer, text + beginIndex + beginLen, MAX_TEXT_BETWEEN_SIZE - 1); else strncpy(buffer, text + beginIndex + beginLen, MAX_TEXT_BUFFER_LENGTH - 1);
} }
} }
@ -1762,8 +1760,74 @@ char *GetTextBetween(const char *text, const char *begin, const char *end)
// Replace text string // Replace text string
// REQUIRES: strstr(), strncpy() // REQUIRES: strstr(), strncpy()
// WARNING: Allocated memory must be manually freed // NOTE: Limited text replace functionality, using static string
char *TextReplace(const char *text, const char *search, const char *replacement) char *TextReplace(const char *text, const char *search, const char *replacement)
{
static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH);
if ((text != NULL) && (search != NULL) && (search[0] != '\0'))
{
if (replacement == NULL) replacement = "";
char *insertPoint = NULL; // Next insert point
char *tempPtr = NULL; // Temp pointer
int textLen = 0; // Text string length
int searchLen = 0; // Search string length of (the string to remove)
int replaceLen = 0; // Replacement length (the string to replace by)
int lastReplacePos = 0; // Distance between next search and end of last replace
int count = 0; // Number of replacements
textLen = TextLength(text);
searchLen = TextLength(search);
replaceLen = TextLength(replacement);
// Count the number of replacements needed
insertPoint = (char *)text;
for (count = 0; (tempPtr = strstr(insertPoint, search)); count++) insertPoint = tempPtr + searchLen;
if ((textLen + count*(replaceLen - searchLen)) < (MAX_TEXT_BUFFER_LENGTH - 1))
{
// TODO: Allow copying data replaced up to maximum buffer size and stop
tempPtr = buffer; // Point to result start
// First time through the loop, all the variable are set correctly from here on,
// - 'temp' points to the end of the result string
// - 'insertPoint' points to the next occurrence of replace in text
// - 'text' points to the remainder of text after "end of replace"
while (count > 0)
{
insertPoint = (char *)strstr(text, search);
lastReplacePos = (int)(insertPoint - text);
memcpy(tempPtr, text, lastReplacePos);
tempPtr += lastReplacePos;
if (replaceLen > 0)
{
memcpy(tempPtr, replacement, replaceLen);
tempPtr += replaceLen;
}
text += (lastReplacePos + searchLen); // Move to next "end of replace"
count--;
}
// Copy remaind text part after replacement to result (pointed by moving temp)
// NOTE: Text pointer internal copy has been updated along the process
strncpy(tempPtr, text, TextLength(text));
}
else TRACELOG(LOG_WARNING, "Text with replacement is longer than internal buffer, use TextReplaceAlloc()");
}
return buffer;
}
// Replace text string
// REQUIRES: strstr(), strncpy()
// WARNING: Allocated memory must be manually freed
char *TextReplaceAlloc(const char *text, const char *search, const char *replacement)
{ {
char *result = NULL; char *result = NULL;
@ -1825,11 +1889,46 @@ char *TextReplace(const char *text, const char *search, const char *replacement)
return result; return result;
} }
// Replace text between two specific strings
// REQUIRES: strncpy()
// NOTE: If (replacement == NULL) removes "begin"[ ]"end" text
char *TextReplaceBetween(const char *text, const char *begin, const char *end, const char *replacement)
{
static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH);
if ((text != NULL) && (begin != NULL) && (end != NULL))
{
int beginIndex = TextFindIndex(text, begin);
if (beginIndex > -1)
{
int beginLen = TextLength(begin);
int endIndex = TextFindIndex(text + beginIndex + beginLen, end);
if (endIndex > -1)
{
endIndex += (beginIndex + beginLen);
int textLen = TextLength(text);
int replaceLen = (replacement == NULL)? 0 : TextLength(replacement);
//int toreplaceLen = endIndex - beginIndex - beginLen;
strncpy(buffer, text, beginIndex + beginLen); // Copy first text part
if (replacement != NULL) strncpy(buffer + beginIndex + beginLen, replacement, replaceLen); // Copy replacement (if provided)
strncpy(buffer + beginIndex + beginLen + replaceLen, text + endIndex, textLen - endIndex); // Copy end text part
}
}
}
return buffer;
}
// Replace text between two specific strings // Replace text between two specific strings
// REQUIRES: strncpy() // REQUIRES: strncpy()
// NOTE: If (replacement == NULL) remove "begin"[ ]"end" text // NOTE: If (replacement == NULL) remove "begin"[ ]"end" text
// WARNING: Returned string must be freed by user // WARNING: Returned string must be freed by user
char *TextReplaceBetween(const char *text, const char *begin, const char *end, const char *replacement) char *TextReplaceBetweenAlloc(const char *text, const char *begin, const char *end, const char *replacement)
{ {
char *result = NULL; char *result = NULL;
@ -1864,6 +1963,34 @@ char *TextReplaceBetween(const char *text, const char *begin, const char *end, c
// Insert text in a specific position, moves all text forward // Insert text in a specific position, moves all text forward
// WARNING: Allocated memory must be manually freed // WARNING: Allocated memory must be manually freed
char *TextInsert(const char *text, const char *insert, int position) char *TextInsert(const char *text, const char *insert, int position)
{
static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH);
if ((text != NULL) && (insert != NULL))
{
int textLen = TextLength(text);
int insertLen = TextLength(insert);
if ((textLen + insertLen) < (MAX_TEXT_BUFFER_LENGTH - 1))
{
// TODO: Allow copying data inserted up to maximum buffer size and stop
for (int i = 0; i < position; i++) buffer[i] = text[i];
for (int i = position; i < insertLen + position; i++) buffer[i] = insert[i - position];
for (int i = (insertLen + position); i < (textLen + insertLen); i++) buffer[i] = text[i];
buffer[textLen + insertLen] = '\0'; // Add EOL
}
else TRACELOG(LOG_WARNING, "Text with inserted string is longer than internal buffer, use TextInserExt()");
}
return buffer;
}
// Insert text in a specific position, moves all text forward
// WARNING: Allocated memory must be manually freed
char *TextInsertAlloc(const char *text, const char *insert, int position)
{ {
char *result = NULL; char *result = NULL;

View File

@ -376,8 +376,7 @@ Image LoadImageAnimFromMemory(const char *fileType, const unsigned char *fileDat
Image image = { 0 }; Image image = { 0 };
int frameCount = 0; int frameCount = 0;
// Security check for input data if ((fileType == NULL) || (fileData == NULL) || (dataSize == 0)) return image; // Security check
if ((fileType == NULL) || (fileData == NULL) || (dataSize == 0)) return image;
#if SUPPORT_FILEFORMAT_GIF #if SUPPORT_FILEFORMAT_GIF
if ((strcmp(fileType, ".gif") == 0) || (strcmp(fileType, ".GIF") == 0)) if ((strcmp(fileType, ".gif") == 0) || (strcmp(fileType, ".GIF") == 0))
@ -621,8 +620,7 @@ bool ExportImage(Image image, const char *fileName)
{ {
int result = 0; int result = 0;
// Security check for input data if ((image.width == 0) || (image.height == 0) || (image.data == NULL)) return result; // Security check
if ((image.width == 0) || (image.height == 0) || (image.data == NULL)) return result;
#if SUPPORT_IMAGE_EXPORT #if SUPPORT_IMAGE_EXPORT
int channels = 4; int channels = 4;
@ -709,8 +707,7 @@ unsigned char *ExportImageToMemory(Image image, const char *fileType, int *dataS
unsigned char *fileData = NULL; unsigned char *fileData = NULL;
*dataSize = 0; *dataSize = 0;
// Security check for input data if ((image.width == 0) || (image.height == 0) || (image.data == NULL)) return fileData; // Security check
if ((image.width == 0) || (image.height == 0) || (image.data == NULL)) return NULL;
int channels = 4; int channels = 4;
@ -734,7 +731,7 @@ unsigned char *ExportImageToMemory(Image image, const char *fileType, int *dataS
// Export image as code file (.h) defining an array of bytes // Export image as code file (.h) defining an array of bytes
bool ExportImageAsCode(Image image, const char *fileName) bool ExportImageAsCode(Image image, const char *fileName)
{ {
bool success = false; bool result = false;
#ifndef TEXT_BYTES_PER_LINE #ifndef TEXT_BYTES_PER_LINE
#define TEXT_BYTES_PER_LINE 20 #define TEXT_BYTES_PER_LINE 20
@ -774,14 +771,14 @@ bool ExportImageAsCode(Image image, const char *fileName)
byteCount += sprintf(txtData + byteCount, "0x%x };\n", ((unsigned char *)image.data)[dataSize - 1]); byteCount += sprintf(txtData + byteCount, "0x%x };\n", ((unsigned char *)image.data)[dataSize - 1]);
// NOTE: Text data size exported is determined by '\0' (NULL) character // NOTE: Text data size exported is determined by '\0' (NULL) character
success = SaveFileText(fileName, txtData); result = SaveFileText(fileName, txtData);
RL_FREE(txtData); RL_FREE(txtData);
if (success != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Image as code exported successfully", fileName); if (result != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Image as code exported successfully", fileName);
else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export image as code", fileName); else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export image as code", fileName);
return success; return result;
} }
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
@ -1536,8 +1533,7 @@ Image ImageFromChannel(Image image, int selectedChannel)
{ {
Image result = { 0 }; Image result = { 0 };
// Security check to avoid program crash if ((image.data == NULL) || (image.width == 0) || (image.height == 0)) return result; // Security check
if ((image.data == NULL) || (image.width == 0) || (image.height == 0)) return result;
// Check selected channel is valid // Check selected channel is valid
if (selectedChannel < 0) if (selectedChannel < 0)
@ -2937,7 +2933,7 @@ void ImageColorReplace(Image *image, Color color, Color replace)
// NOTE: Memory allocated should be freed using UnloadImageColors(); // NOTE: Memory allocated should be freed using UnloadImageColors();
Color *LoadImageColors(Image image) Color *LoadImageColors(Image image)
{ {
if ((image.width == 0) || (image.height == 0)) return NULL; if ((image.width == 0) || (image.height == 0)) return NULL; // Security check
Color *pixels = (Color *)RL_MALLOC(image.width*image.height*sizeof(Color)); Color *pixels = (Color *)RL_MALLOC(image.width*image.height*sizeof(Color));
@ -4903,44 +4899,44 @@ Vector3 ColorToHSV(Color color)
float max = 0.0f; float max = 0.0f;
float delta = 0.0f; float delta = 0.0f;
min = rgb.x < rgb.y? rgb.x : rgb.y; min = (rgb.x < rgb.y)? rgb.x : rgb.y;
min = min < rgb.z? min : rgb.z; min = (min < rgb.z)? min : rgb.z;
max = rgb.x > rgb.y? rgb.x : rgb.y; max = (rgb.x > rgb.y)? rgb.x : rgb.y;
max = max > rgb.z? max : rgb.z; max = (max > rgb.z)? max : rgb.z;
hsv.z = max; // Value hsv.z = max; // Value
delta = max - min; delta = max - min;
if (delta < 0.00001f) if (delta < 0.00001f)
{ {
hsv.y = 0.0f; hsv.y = 0.0f;
hsv.x = 0.0f; // Undefined, maybe NAN? hsv.x = 0.0f; // Undefined, maybe NAN?
return hsv; return hsv;
} }
if (max > 0.0f) if (max > 0.0f)
{ {
// NOTE: If max is 0, this divide would cause a crash // NOTE: If max is 0, this divide would cause a crash
hsv.y = (delta/max); // Saturation hsv.y = (delta/max); // Saturation
} }
else else
{ {
// NOTE: If max is 0, then r = g = b = 0, s = 0, h is undefined // NOTE: If max is 0, then r = g = b = 0, s = 0, h is undefined
hsv.y = 0.0f; hsv.y = 0.0f;
hsv.x = NAN; // Undefined hsv.x = NAN; // Undefined
return hsv; return hsv;
} }
// NOTE: Comparing float values could not work properly // NOTE: Comparing float values could not work properly
if (rgb.x >= max) hsv.x = (rgb.y - rgb.z)/delta; // Between yellow & magenta if (rgb.x >= max) hsv.x = (rgb.y - rgb.z)/delta; // Between yellow & magenta
else else
{ {
if (rgb.y >= max) hsv.x = 2.0f + (rgb.z - rgb.x)/delta; // Between cyan & yellow if (rgb.y >= max) hsv.x = 2.0f + (rgb.z - rgb.x)/delta; // Between cyan & yellow
else hsv.x = 4.0f + (rgb.x - rgb.y)/delta; // Between magenta & cyan else hsv.x = 4.0f + (rgb.x - rgb.y)/delta; // Between magenta & cyan
} }
hsv.x *= 60.0f; // Convert to degrees hsv.x *= 60.0f; // Convert to degrees
if (hsv.x < 0.0f) hsv.x += 360.0f; if (hsv.x < 0.0f) hsv.x += 360.0f;
@ -5093,7 +5089,7 @@ Color ColorAlpha(Color color, float alpha)
// Get src alpha-blended into dst color with tint // Get src alpha-blended into dst color with tint
Color ColorAlphaBlend(Color dst, Color src, Color tint) Color ColorAlphaBlend(Color dst, Color src, Color tint)
{ {
Color out = WHITE; Color result = WHITE;
// Apply color tint to source color // Apply color tint to source color
src.r = (unsigned char)(((unsigned int)src.r*((unsigned int)tint.r+1)) >> 8); src.r = (unsigned char)(((unsigned int)src.r*((unsigned int)tint.r+1)) >> 8);
@ -5104,24 +5100,24 @@ Color ColorAlphaBlend(Color dst, Color src, Color tint)
//#define COLORALPHABLEND_FLOAT //#define COLORALPHABLEND_FLOAT
#define COLORALPHABLEND_INTEGERS #define COLORALPHABLEND_INTEGERS
#if defined(COLORALPHABLEND_INTEGERS) #if defined(COLORALPHABLEND_INTEGERS)
if (src.a == 0) out = dst; if (src.a == 0) result = dst;
else if (src.a == 255) out = src; else if (src.a == 255) result = src;
else else
{ {
unsigned int alpha = (unsigned int)src.a + 1; // Shifting by 8 (dividing by 256), so need to take that excess into account unsigned int alpha = (unsigned int)src.a + 1; // Shifting by 8 (dividing by 256), so need to take that excess into account
out.a = (unsigned char)(((unsigned int)alpha*256 + (unsigned int)dst.a*(256 - alpha)) >> 8); result.a = (unsigned char)(((unsigned int)alpha*256 + (unsigned int)dst.a*(256 - alpha)) >> 8);
if (out.a > 0) if (result.a > 0)
{ {
out.r = (unsigned char)((((unsigned int)src.r*alpha*256 + (unsigned int)dst.r*(unsigned int)dst.a*(256 - alpha))/out.a) >> 8); result.r = (unsigned char)((((unsigned int)src.r*alpha*256 + (unsigned int)dst.r*(unsigned int)dst.a*(256 - alpha))/result.a) >> 8);
out.g = (unsigned char)((((unsigned int)src.g*alpha*256 + (unsigned int)dst.g*(unsigned int)dst.a*(256 - alpha))/out.a) >> 8); result.g = (unsigned char)((((unsigned int)src.g*alpha*256 + (unsigned int)dst.g*(unsigned int)dst.a*(256 - alpha))/result.a) >> 8);
out.b = (unsigned char)((((unsigned int)src.b*alpha*256 + (unsigned int)dst.b*(unsigned int)dst.a*(256 - alpha))/out.a) >> 8); result.b = (unsigned char)((((unsigned int)src.b*alpha*256 + (unsigned int)dst.b*(unsigned int)dst.a*(256 - alpha))/result.a) >> 8);
} }
} }
#endif #endif
#if defined(COLORALPHABLEND_FLOAT) #if defined(COLORALPHABLEND_FLOAT)
if (src.a == 0) out = dst; if (src.a == 0) result = dst;
else if (src.a == 255) out = src; else if (src.a == 255) result = src;
else else
{ {
Vector4 fdst = ColorNormalize(dst); Vector4 fdst = ColorNormalize(dst);
@ -5138,11 +5134,11 @@ Color ColorAlphaBlend(Color dst, Color src, Color tint)
fout.z = (fsrc.z*fsrc.w + fdst.z*fdst.w*(1 - fsrc.w))/fout.w; fout.z = (fsrc.z*fsrc.w + fdst.z*fdst.w*(1 - fsrc.w))/fout.w;
} }
out = (Color){ (unsigned char)(fout.x*255.0f), (unsigned char)(fout.y*255.0f), (unsigned char)(fout.z*255.0f), (unsigned char)(fout.w*255.0f) }; result = (Color){ (unsigned char)(fout.x*255.0f), (unsigned char)(fout.y*255.0f), (unsigned char)(fout.z*255.0f), (unsigned char)(fout.w*255.0f) };
} }
#endif #endif
return out; return result;
} }
// Get color lerp interpolation between two colors, factor [0.0f..1.0f] // Get color lerp interpolation between two colors, factor [0.0f..1.0f]

View File

@ -68,6 +68,7 @@
#define REXM_MAX_BUFFER_SIZE (2*1024*1024) // 2MB #define REXM_MAX_BUFFER_SIZE (2*1024*1024) // 2MB
#define REXM_MAX_RESOURCE_PATHS 256 #define REXM_MAX_RESOURCE_PATHS 256
#define REXM_MAX_RESOURCE_PATH_LENGTH 256
// Create local commit with changes on example renaming // Create local commit with changes on example renaming
//#define RENAME_AUTO_COMMIT_CREATION //#define RENAME_AUTO_COMMIT_CREATION
@ -469,12 +470,12 @@ int main(int argc, char *argv[])
int exIndex = TextFindIndex(exText, "/****************"); int exIndex = TextFindIndex(exText, "/****************");
// Update required info with some defaults // Update required info with some defaults
exTextUpdated[0] = TextReplace(exText + exIndex, "<module>", exCategory); exTextUpdated[0] = TextReplaceAlloc(exText + exIndex, "<module>", exCategory);
exTextUpdated[1] = TextReplace(exTextUpdated[0], "<name>", exName + strlen(exCategory) + 1); exTextUpdated[1] = TextReplaceAlloc(exTextUpdated[0], "<name>", exName + strlen(exCategory) + 1);
//TextReplace(newExample, "<user_name>", "Ray"); //TextReplaceAlloc(newExample, "<user_name>", "Ray");
//TextReplace(newExample, "@<user_github>", "@raysan5"); //TextReplaceAlloc(newExample, "@<user_github>", "@raysan5");
//TextReplace(newExample, "<year_created>", 2025); //TextReplaceAlloc(newExample, "<year_created>", 2025);
//TextReplace(newExample, "<year_updated>", 2025); //TextReplaceAlloc(newExample, "<year_updated>", 2025);
SaveFileText(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName), exTextUpdated[1]); SaveFileText(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName), exTextUpdated[1]);
for (int i = 0; i < 6; i++) { MemFree(exTextUpdated[i]); exTextUpdated[i] = NULL; } for (int i = 0; i < 6; i++) { MemFree(exTextUpdated[i]); exTextUpdated[i] = NULL; }
@ -541,8 +542,6 @@ int main(int argc, char *argv[])
else LOG("WARNING: Example resource must be placed in 'resources' directory next to .c file\n"); else LOG("WARNING: Example resource must be placed in 'resources' directory next to .c file\n");
} }
else LOG("WARNING: Example resource can not be found in: %s\n", TextFormat("%s/%s", GetDirectoryPath(inFileName), resPathUpdated)); else LOG("WARNING: Example resource can not be found in: %s\n", TextFormat("%s/%s", GetDirectoryPath(inFileName), resPathUpdated));
RL_FREE(resPathUpdated);
} }
} }
else else
@ -863,9 +862,7 @@ int main(int argc, char *argv[])
for (int v = 0; v < 3; v++) for (int v = 0; v < 3; v++)
{ {
char *resPathUpdated = TextReplace(resPaths[r], "glsl%i", TextFormat("glsl%i", glslVer[v])); FileRemove(TextFormat("%s/%s/%s", exBasePath, exCategory, TextReplace(resPaths[r], "glsl%i", TextFormat("glsl%i", glslVer[v]))));
FileRemove(TextFormat("%s/%s/%s", exBasePath, exCategory, resPathUpdated));
RL_FREE(resPathUpdated);
} }
} }
else FileRemove(TextFormat("%s/%s/%s", exBasePath, exCategory, resPaths[r])); else FileRemove(TextFormat("%s/%s/%s", exBasePath, exCategory, resPaths[r]));
@ -1164,7 +1161,6 @@ int main(int argc, char *argv[])
// Logging missing resources for convenience // Logging missing resources for convenience
LOG("WARNING: [%s] Missing resource: %s\n", exInfo->name, resPathUpdated); LOG("WARNING: [%s] Missing resource: %s\n", exInfo->name, resPathUpdated);
} }
RL_FREE(resPathUpdated);
} }
} }
else else
@ -1577,13 +1573,13 @@ int main(int argc, char *argv[])
" SaveFileText(\"outputLogFileName\", logText);\n" " SaveFileText(\"outputLogFileName\", logText);\n"
" emscripten_run_script(\"saveFileFromMEMFSToDisk('outputLogFileName','outputLogFileName')\");\n\n" " emscripten_run_script(\"saveFileFromMEMFSToDisk('outputLogFileName','outputLogFileName')\");\n\n"
" return 0"; " return 0";
char *returnReplaceTextUpdated = TextReplace(returnReplaceText, "outputLogFileName", TextFormat("%s.log", exName)); char *returnReplaceTextUpdated = TextReplaceAlloc(returnReplaceText, "outputLogFileName", TextFormat("%s.log", exName));
char *srcTextUpdated[4] = { 0 }; char *srcTextUpdated[4] = { 0 };
srcTextUpdated[0] = TextReplace(srcText, "int main(void)\n{", mainReplaceText); srcTextUpdated[0] = TextReplaceAlloc(srcText, "int main(void)\n{", mainReplaceText);
srcTextUpdated[1] = TextReplace(srcTextUpdated[0], "WindowShouldClose()", "WindowShouldClose() && (testFramesCount < requestedTestFrames)"); srcTextUpdated[1] = TextReplaceAlloc(srcTextUpdated[0], "WindowShouldClose()", "WindowShouldClose() && (testFramesCount < requestedTestFrames)");
srcTextUpdated[2] = TextReplace(srcTextUpdated[1], "EndDrawing();", "EndDrawing(); testFramesCount++;"); srcTextUpdated[2] = TextReplaceAlloc(srcTextUpdated[1], "EndDrawing();", "EndDrawing(); testFramesCount++;");
srcTextUpdated[3] = TextReplace(srcTextUpdated[2], " return 0", returnReplaceTextUpdated); srcTextUpdated[3] = TextReplaceAlloc(srcTextUpdated[2], " return 0", returnReplaceTextUpdated);
MemFree(returnReplaceTextUpdated); MemFree(returnReplaceTextUpdated);
UnloadFileText(srcText); UnloadFileText(srcText);
@ -1633,9 +1629,9 @@ int main(int argc, char *argv[])
" if ((argc > 1) && (argc == 3) && (strcmp(argv[1], \"--frames\") != 0)) requestedTestFrames = atoi(argv[2]);\n"; " if ((argc > 1) && (argc == 3) && (strcmp(argv[1], \"--frames\") != 0)) requestedTestFrames = atoi(argv[2]);\n";
char *srcTextUpdated[3] = { 0 }; char *srcTextUpdated[3] = { 0 };
srcTextUpdated[0] = TextReplace(srcText, "int main(void)\n{", mainReplaceText); srcTextUpdated[0] = TextReplaceAlloc(srcText, "int main(void)\n{", mainReplaceText);
srcTextUpdated[1] = TextReplace(srcTextUpdated[0], "WindowShouldClose()", "WindowShouldClose() && (testFramesCount < requestedTestFrames)"); srcTextUpdated[1] = TextReplaceAlloc(srcTextUpdated[0], "WindowShouldClose()", "WindowShouldClose() && (testFramesCount < requestedTestFrames)");
srcTextUpdated[2] = TextReplace(srcTextUpdated[1], "EndDrawing();", "EndDrawing(); testFramesCount++;"); srcTextUpdated[2] = TextReplaceAlloc(srcTextUpdated[1], "EndDrawing();", "EndDrawing(); testFramesCount++;");
UnloadFileText(srcText); UnloadFileText(srcText);
SaveFileText(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName), srcTextUpdated[2]); SaveFileText(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName), srcTextUpdated[2]);
@ -2042,7 +2038,7 @@ static int UpdateRequiredFiles(void)
// In this case, we focus on web building for: glsl100 // In this case, we focus on web building for: glsl100
if (TextFindIndex(resPaths[r], "glsl%i") > -1) if (TextFindIndex(resPaths[r], "glsl%i") > -1)
{ {
char *resPathUpdated = TextReplace(resPaths[r], "glsl%i", "glsl100"); char *resPathUpdated = TextReplaceAlloc(resPaths[r], "glsl%i", "glsl100");
memset(resPaths[r], 0, 256); memset(resPaths[r], 0, 256);
strcpy(resPaths[r], resPathUpdated); strcpy(resPaths[r], resPathUpdated);
RL_FREE(resPathUpdated); RL_FREE(resPathUpdated);
@ -2147,7 +2143,7 @@ static int UpdateRequiredFiles(void)
{ {
mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, TextFormat("\n### category: shaders [%i]\n\n", exCollectionCount)); mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, TextFormat("\n### category: shaders [%i]\n\n", exCollectionCount));
mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex,
"Examples using raylib shaders functionality, including shaders loading, parameters configuration and drawing using them (model shaders and postprocessing shaders). This functionality is directly provided by raylib [rlgl](../src/rlgl.c) module.\n\n"); "Examples using raylib shaders functionality, including shaders loading, parameters configuration and drawing using them (model shaders and postprocessing shaders). This functionality is directly provided by raylib [rlgl](../src/rlgl.h) module.\n\n");
} }
else if (i == 6) // "audio" else if (i == 6) // "audio"
{ {
@ -2498,25 +2494,21 @@ static void SortExampleByName(rlExampleInfo *items, int count)
qsort(items, count, sizeof(rlExampleInfo), rlExampleInfoCompare); qsort(items, count, sizeof(rlExampleInfo), rlExampleInfoCompare);
} }
// Scan resource paths in example file // Scan asset paths from a source code file (raylib)
// WARNING: Supported resource file extensions is hardcoded by used file types // WARNING: Supported asset file extensions are hardcoded by used file types
// but new examples could require other file extensions to be added, // but new examples could require other file extensions to be added
// maybe it should look for '.xxx")' patterns instead static char **LoadExampleResourcePaths(const char *srcFilePath, int *resPathCount)
// TODO: WARNING: Some resources could require linked resources: .fnt --> .png, .mtl --> .png, .gltf --> .png, ...
static char **LoadExampleResourcePaths(const char *filePath, int *resPathCount)
{ {
#define REXM_MAX_RESOURCE_PATH_LEN 256
char **paths = (char **)RL_CALLOC(REXM_MAX_RESOURCE_PATHS, sizeof(char **)); char **paths = (char **)RL_CALLOC(REXM_MAX_RESOURCE_PATHS, sizeof(char **));
for (int i = 0; i < REXM_MAX_RESOURCE_PATHS; i++) paths[i] = (char *)RL_CALLOC(REXM_MAX_RESOURCE_PATH_LEN, sizeof(char)); for (int i = 0; i < REXM_MAX_RESOURCE_PATHS; i++) paths[i] = (char *)RL_CALLOC(REXM_MAX_RESOURCE_PATH_LENGTH, sizeof(char));
int resCounter = 0; int resCounter = 0;
char *code = LoadFileText(filePath); char *code = LoadFileText(srcFilePath);
if (code != NULL) if (code != NULL)
{ {
// Resources extensions to check // Resources extensions to check
const char *exts[] = { ".png", ".bmp", ".jpg", ".qoi", ".gif", ".raw", ".hdr", ".ttf", ".fnt", ".wav", ".ogg", ".mp3", ".flac", ".mod", ".qoa", ".obj", ".iqm", ".glb", ".m3d", ".vox", ".vs", ".fs", ".txt" }; const char *exts[] = { ".png", ".bmp", ".jpg", ".qoi", ".gif", ".raw", ".hdr", ".ttf", ".fnt", ".wav", ".ogg", ".mp3", ".flac", ".mod", ".xm", ".qoa", ".obj", ".iqm", ".glb", ".m3d", ".vox", ".vs", ".fs", ".txt" };
const int extCount = sizeof(exts)/sizeof(char *); const int extCount = sizeof(exts)/sizeof(char *);
char *ptr = code; char *ptr = code;
@ -2542,9 +2534,9 @@ static char **LoadExampleResourcePaths(const char *filePath, int *resPathCount)
!((functionIndex05 != -1) && (functionIndex05 < 40))) // Not found SaveFileText() before "" !((functionIndex05 != -1) && (functionIndex05 < 40))) // Not found SaveFileText() before ""
{ {
int len = (int)(end - start); int len = (int)(end - start);
if ((len > 0) && (len < REXM_MAX_RESOURCE_PATH_LEN)) if ((len > 0) && (len < REXM_MAX_RESOURCE_PATH_LENGTH))
{ {
char buffer[REXM_MAX_RESOURCE_PATH_LEN] = { 0 }; char buffer[REXM_MAX_RESOURCE_PATH_LENGTH] = { 0 };
strncpy(buffer, start, len); strncpy(buffer, start, len);
buffer[len] = '\0'; buffer[len] = '\0';
@ -2580,6 +2572,29 @@ static char **LoadExampleResourcePaths(const char *filePath, int *resPathCount)
UnloadFileText(code); UnloadFileText(code);
} }
/*
// WARNING: Some resources could require linked resources: .fnt --> .png, .mtl --> .png, .gltf --> .png
// So doing a recursive pass to scan possible files with second resources
int currentAssetCounter = resCounter;
for (int i = 0; i < currentAssetCounter; i++)
{
if (IsFileExtension(paths[i], ".fnt;.gltf"))
{
int assetCount2 = 0;
// ERROR: srcFilePath changes on rcursive call and TextFormat() static arrays are override
char **assetPaths2 = LoadExampleResourcePaths(TextFormat("%s/%s", GetDirectoryPath(srcFilePath), paths[i]), &assetCount2);
for (int j = 0; j < assetCount2; j++)
{
strcpy(paths[resCounter], TextFormat("%s/%s", GetDirectoryPath(paths[i]) + 2, assetPaths2[j]));
resCounter++;
}
UnloadExampleResourcePaths(assetPaths2);
}
}
*/
*resPathCount = resCounter; *resPathCount = resCounter;
return paths; return paths;
} }
@ -2815,7 +2830,7 @@ static void UpdateSourceMetadata(const char *exSrcPath, const rlExampleInfo *inf
// Update example header title (line #3 - ALWAYS) // Update example header title (line #3 - ALWAYS)
// String: "* raylib [shaders] example - texture drawing" // String: "* raylib [shaders] example - texture drawing"
exTextUpdated[0] = TextReplaceBetween(exTextUpdatedPtr, "* raylib [", "\n", exTextUpdated[0] = TextReplaceBetweenAlloc(exTextUpdatedPtr, "* raylib [", "\n",
TextFormat("%s] example - %s", info->category, exNameFormated)); TextFormat("%s] example - %s", info->category, exNameFormated));
if (exTextUpdated[0] != NULL) exTextUpdatedPtr = exTextUpdated[0]; if (exTextUpdated[0] != NULL) exTextUpdatedPtr = exTextUpdated[0];
@ -2829,13 +2844,13 @@ static void UpdateSourceMetadata(const char *exSrcPath, const rlExampleInfo *inf
if (i < info->stars) strcpy(starsText + 3*i, ""); if (i < info->stars) strcpy(starsText + 3*i, "");
else strcpy(starsText + 3*i, ""); else strcpy(starsText + 3*i, "");
} }
exTextUpdated[1] = TextReplaceBetween(exTextUpdatedPtr, "* Example complexity rating: [", "/4\n", exTextUpdated[1] = TextReplaceBetweenAlloc(exTextUpdatedPtr, "* Example complexity rating: [", "/4\n",
TextFormat("%s] %i", starsText, info->stars)); TextFormat("%s] %i", starsText, info->stars));
if (exTextUpdated[1] != NULL) exTextUpdatedPtr = exTextUpdated[1]; if (exTextUpdated[1] != NULL) exTextUpdatedPtr = exTextUpdated[1];
// Update example creation/update raylib versions // Update example creation/update raylib versions
// String: "* Example originally created with raylib 2.0, last time updated with raylib 3.7 // String: "* Example originally created with raylib 2.0, last time updated with raylib 3.7
exTextUpdated[2] = TextReplaceBetween(exTextUpdatedPtr, "* Example originally created with raylib ", "\n", exTextUpdated[2] = TextReplaceBetweenAlloc(exTextUpdatedPtr, "* Example originally created with raylib ", "\n",
TextFormat("%s, last time updated with raylib %s", info->verCreated, info->verUpdated)); TextFormat("%s, last time updated with raylib %s", info->verCreated, info->verUpdated));
if (exTextUpdated[2] != NULL) exTextUpdatedPtr = exTextUpdated[2]; if (exTextUpdated[2] != NULL) exTextUpdatedPtr = exTextUpdated[2];
@ -2843,27 +2858,27 @@ static void UpdateSourceMetadata(const char *exSrcPath, const rlExampleInfo *inf
// String: "* Copyright (c) 2019-2026 Contributor Name (@github_user) and Ramon Santamaria (@raysan5)" // String: "* Copyright (c) 2019-2026 Contributor Name (@github_user) and Ramon Santamaria (@raysan5)"
if (info->yearCreated == info->yearReviewed) if (info->yearCreated == info->yearReviewed)
{ {
exTextUpdated[3] = TextReplaceBetween(exTextUpdatedPtr, "Copyright (c) ", ")", exTextUpdated[3] = TextReplaceBetweenAlloc(exTextUpdatedPtr, "Copyright (c) ", ")",
TextFormat("%i %s (@%s", info->yearCreated, info->author, info->authorGitHub)); TextFormat("%i %s (@%s", info->yearCreated, info->author, info->authorGitHub));
if (exTextUpdated[3] != NULL) exTextUpdatedPtr = exTextUpdated[3]; if (exTextUpdated[3] != NULL) exTextUpdatedPtr = exTextUpdated[3];
} }
else else
{ {
exTextUpdated[3] = TextReplaceBetween(exTextUpdatedPtr, "Copyright (c) ", ")", exTextUpdated[3] = TextReplaceBetweenAlloc(exTextUpdatedPtr, "Copyright (c) ", ")",
TextFormat("%i-%i %s (@%s", info->yearCreated, info->yearReviewed, info->author, info->authorGitHub)); TextFormat("%i-%i %s (@%s", info->yearCreated, info->yearReviewed, info->author, info->authorGitHub));
if (exTextUpdated[3] != NULL) exTextUpdatedPtr = exTextUpdated[3]; if (exTextUpdated[3] != NULL) exTextUpdatedPtr = exTextUpdated[3];
} }
// Update window title // Update window title
// String: "InitWindow(screenWidth, screenHeight, "raylib [shaders] example - texture drawing");" // String: "InitWindow(screenWidth, screenHeight, "raylib [shaders] example - texture drawing");"
exTextUpdated[4] = TextReplaceBetween(exTextUpdated[3], "InitWindow(screenWidth, screenHeight, \"", "\");", exTextUpdated[4] = TextReplaceBetweenAlloc(exTextUpdated[3], "InitWindow(screenWidth, screenHeight, \"", "\");",
TextFormat("raylib [%s] example - %s", info->category, exNameFormated)); TextFormat("raylib [%s] example - %s", info->category, exNameFormated));
if (exTextUpdated[4] != NULL) exTextUpdatedPtr = exTextUpdated[4]; if (exTextUpdated[4] != NULL) exTextUpdatedPtr = exTextUpdated[4];
// Update contributors names // Update contributors names
// String: "* Example contributed by Contributor Name (@github_user) and reviewed by Ramon Santamaria (@raysan5)" // String: "* Example contributed by Contributor Name (@github_user) and reviewed by Ramon Santamaria (@raysan5)"
// WARNING: Not all examples are contributed by someone, so the result of this replace can be NULL (string not found) // WARNING: Not all examples are contributed by someone, so the result of this replace can be NULL (string not found)
exTextUpdated[5] = TextReplaceBetween(exTextUpdatedPtr, "* Example contributed by ", ")", exTextUpdated[5] = TextReplaceBetweenAlloc(exTextUpdatedPtr, "* Example contributed by ", ")",
TextFormat("%s (@%s", info->author, info->authorGitHub)); TextFormat("%s (@%s", info->author, info->authorGitHub));
if (exTextUpdated[5] != NULL) exTextUpdatedPtr = exTextUpdated[5]; if (exTextUpdated[5] != NULL) exTextUpdatedPtr = exTextUpdated[5];
@ -2910,14 +2925,14 @@ static void UpdateWebMetadata(const char *exHtmlPath, const char *exFilePath)
UnloadFileText(exText); UnloadFileText(exText);
// Update example.html required text // Update example.html required text
exHtmlTextUpdated[0] = TextReplace(exHtmlText, "raylib web game", exTitle); exHtmlTextUpdated[0] = TextReplaceAlloc(exHtmlText, "raylib web game", exTitle);
exHtmlTextUpdated[1] = TextReplace(exHtmlTextUpdated[0], "New raylib web videogame, developed using raylib videogames library", exDescription); exHtmlTextUpdated[1] = TextReplaceAlloc(exHtmlTextUpdated[0], "New raylib web videogame, developed using raylib videogames library", exDescription);
exHtmlTextUpdated[2] = TextReplace(exHtmlTextUpdated[1], "https://www.raylib.com/common/raylib_logo.png", exHtmlTextUpdated[2] = TextReplaceAlloc(exHtmlTextUpdated[1], "https://www.raylib.com/common/raylib_logo.png",
TextFormat("https://raw.githubusercontent.com/raysan5/raylib/master/examples/%s/%s.png", exCategory, exName)); TextFormat("https://raw.githubusercontent.com/raysan5/raylib/master/examples/%s/%s.png", exCategory, exName));
exHtmlTextUpdated[3] = TextReplace(exHtmlTextUpdated[2], "https://www.raylib.com/games.html", exHtmlTextUpdated[3] = TextReplaceAlloc(exHtmlTextUpdated[2], "https://www.raylib.com/games.html",
TextFormat("https://www.raylib.com/examples/%s/%s.html", exCategory, exName)); TextFormat("https://www.raylib.com/examples/%s/%s.html", exCategory, exName));
exHtmlTextUpdated[4] = TextReplace(exHtmlTextUpdated[3], "raylib - example", TextFormat("raylib - %s", exName)); // og:site_name exHtmlTextUpdated[4] = TextReplaceAlloc(exHtmlTextUpdated[3], "raylib - example", TextFormat("raylib - %s", exName)); // og:site_name
exHtmlTextUpdated[5] = TextReplace(exHtmlTextUpdated[4], "https://github.com/raysan5/raylib", exHtmlTextUpdated[5] = TextReplaceAlloc(exHtmlTextUpdated[4], "https://github.com/raysan5/raylib",
TextFormat("https://github.com/raysan5/raylib/blob/master/examples/%s/%s.c", exCategory, exName)); TextFormat("https://github.com/raysan5/raylib/blob/master/examples/%s/%s.c", exCategory, exName));
SaveFileText(exHtmlPathCopy, exHtmlTextUpdated[5]); SaveFileText(exHtmlPathCopy, exHtmlTextUpdated[5]);

View File

@ -4685,7 +4685,7 @@
}, },
{ {
"name": "LoadDirectoryFiles", "name": "LoadDirectoryFiles",
"description": "Load directory filepaths", "description": "Load directory filepaths, files and directories, no subdirs scan",
"returnType": "FilePathList", "returnType": "FilePathList",
"params": [ "params": [
{ {
@ -4696,7 +4696,7 @@
}, },
{ {
"name": "LoadDirectoryFilesEx", "name": "LoadDirectoryFilesEx",
"description": "Load directory filepaths with extension filtering and recursive directory scan. Use 'DIR' in the filter string to include directories in the result", "description": "Load directory filepaths with extension filtering and subdir scan; some filters available: "*.*", "FILES*", "DIRS*"",
"returnType": "FilePathList", "returnType": "FilePathList",
"params": [ "params": [
{ {
@ -7231,7 +7231,7 @@
}, },
{ {
"name": "ExportImageToMemory", "name": "ExportImageToMemory",
"description": "Export image to memory buffer", "description": "Export image to memory buffer, memory must be MemFree()",
"returnType": "unsigned char *", "returnType": "unsigned char *",
"params": [ "params": [
{ {
@ -9946,7 +9946,26 @@
}, },
{ {
"name": "TextReplace", "name": "TextReplace",
"description": "Replace text string (WARNING: memory must be freed!)", "description": "Replace text string with new string",
"returnType": "char *",
"params": [
{
"type": "const char *",
"name": "text"
},
{
"type": "const char *",
"name": "search"
},
{
"type": "const char *",
"name": "replacement"
}
]
},
{
"name": "TextReplaceAlloc",
"description": "Replace text string with new string, memory must be MemFree()",
"returnType": "char *", "returnType": "char *",
"params": [ "params": [
{ {
@ -9965,7 +9984,30 @@
}, },
{ {
"name": "TextReplaceBetween", "name": "TextReplaceBetween",
"description": "Replace text between two specific strings (WARNING: memory must be freed!)", "description": "Replace text between two specific strings",
"returnType": "char *",
"params": [
{
"type": "const char *",
"name": "text"
},
{
"type": "const char *",
"name": "begin"
},
{
"type": "const char *",
"name": "end"
},
{
"type": "const char *",
"name": "replacement"
}
]
},
{
"name": "TextReplaceBetweenAlloc",
"description": "Replace text between two specific strings, memory must be MemFree()",
"returnType": "char *", "returnType": "char *",
"params": [ "params": [
{ {
@ -9988,7 +10030,26 @@
}, },
{ {
"name": "TextInsert", "name": "TextInsert",
"description": "Insert text in a position (WARNING: memory must be freed!)", "description": "Insert text in a defined byte position",
"returnType": "char *",
"params": [
{
"type": "const char *",
"name": "text"
},
{
"type": "const char *",
"name": "insert"
},
{
"type": "int",
"name": "position"
}
]
},
{
"name": "TextInsertAlloc",
"description": "Insert text in a defined byte position, memory must be MemFree()",
"returnType": "char *", "returnType": "char *",
"params": [ "params": [
{ {
@ -10820,60 +10881,6 @@
} }
] ]
}, },
{
"name": "DrawModelPoints",
"description": "Draw a model as points",
"returnType": "void",
"params": [
{
"type": "Model",
"name": "model"
},
{
"type": "Vector3",
"name": "position"
},
{
"type": "float",
"name": "scale"
},
{
"type": "Color",
"name": "tint"
}
]
},
{
"name": "DrawModelPointsEx",
"description": "Draw a model as points with extended parameters",
"returnType": "void",
"params": [
{
"type": "Model",
"name": "model"
},
{
"type": "Vector3",
"name": "position"
},
{
"type": "Vector3",
"name": "rotationAxis"
},
{
"type": "float",
"name": "rotationAngle"
},
{
"type": "Vector3",
"name": "scale"
},
{
"type": "Color",
"name": "tint"
}
]
},
{ {
"name": "DrawBoundingBox", "name": "DrawBoundingBox",
"description": "Draw bounding box (wires)", "description": "Draw bounding box (wires)",

View File

@ -4199,7 +4199,7 @@ return {
}, },
{ {
name = "LoadDirectoryFiles", name = "LoadDirectoryFiles",
description = "Load directory filepaths", description = "Load directory filepaths, files and directories, no subdirs scan",
returnType = "FilePathList", returnType = "FilePathList",
params = { params = {
{type = "const char *", name = "dirPath"} {type = "const char *", name = "dirPath"}
@ -4207,7 +4207,7 @@ return {
}, },
{ {
name = "LoadDirectoryFilesEx", name = "LoadDirectoryFilesEx",
description = "Load directory filepaths with extension filtering and recursive directory scan. Use 'DIR' in the filter string to include directories in the result", description = "Load directory filepaths with extension filtering and subdir scan; some filters available: "*.*", "FILES*", "DIRS*"",
returnType = "FilePathList", returnType = "FilePathList",
params = { params = {
{type = "const char *", name = "basePath"}, {type = "const char *", name = "basePath"},
@ -5596,7 +5596,7 @@ return {
}, },
{ {
name = "ExportImageToMemory", name = "ExportImageToMemory",
description = "Export image to memory buffer", description = "Export image to memory buffer, memory must be MemFree()",
returnType = "unsigned char *", returnType = "unsigned char *",
params = { params = {
{type = "Image", name = "image"}, {type = "Image", name = "image"},
@ -7045,7 +7045,17 @@ return {
}, },
{ {
name = "TextReplace", name = "TextReplace",
description = "Replace text string (WARNING: memory must be freed!)", description = "Replace text string with new string",
returnType = "char *",
params = {
{type = "const char *", name = "text"},
{type = "const char *", name = "search"},
{type = "const char *", name = "replacement"}
}
},
{
name = "TextReplaceAlloc",
description = "Replace text string with new string, memory must be MemFree()",
returnType = "char *", returnType = "char *",
params = { params = {
{type = "const char *", name = "text"}, {type = "const char *", name = "text"},
@ -7055,7 +7065,18 @@ return {
}, },
{ {
name = "TextReplaceBetween", name = "TextReplaceBetween",
description = "Replace text between two specific strings (WARNING: memory must be freed!)", description = "Replace text between two specific strings",
returnType = "char *",
params = {
{type = "const char *", name = "text"},
{type = "const char *", name = "begin"},
{type = "const char *", name = "end"},
{type = "const char *", name = "replacement"}
}
},
{
name = "TextReplaceBetweenAlloc",
description = "Replace text between two specific strings, memory must be MemFree()",
returnType = "char *", returnType = "char *",
params = { params = {
{type = "const char *", name = "text"}, {type = "const char *", name = "text"},
@ -7066,7 +7087,17 @@ return {
}, },
{ {
name = "TextInsert", name = "TextInsert",
description = "Insert text in a position (WARNING: memory must be freed!)", description = "Insert text in a defined byte position",
returnType = "char *",
params = {
{type = "const char *", name = "text"},
{type = "const char *", name = "insert"},
{type = "int", name = "position"}
}
},
{
name = "TextInsertAlloc",
description = "Insert text in a defined byte position, memory must be MemFree()",
returnType = "char *", returnType = "char *",
params = { params = {
{type = "const char *", name = "text"}, {type = "const char *", name = "text"},
@ -7493,30 +7524,6 @@ return {
{type = "Color", name = "tint"} {type = "Color", name = "tint"}
} }
}, },
{
name = "DrawModelPoints",
description = "Draw a model as points",
returnType = "void",
params = {
{type = "Model", name = "model"},
{type = "Vector3", name = "position"},
{type = "float", name = "scale"},
{type = "Color", name = "tint"}
}
},
{
name = "DrawModelPointsEx",
description = "Draw a model as points with extended parameters",
returnType = "void",
params = {
{type = "Model", name = "model"},
{type = "Vector3", name = "position"},
{type = "Vector3", name = "rotationAxis"},
{type = "float", name = "rotationAngle"},
{type = "Vector3", name = "scale"},
{type = "Color", name = "tint"}
}
},
{ {
name = "DrawBoundingBox", name = "DrawBoundingBox",
description = "Draw bounding box (wires)", description = "Draw bounding box (wires)",

File diff suppressed because it is too large Load Diff

View File

@ -682,7 +682,7 @@
<Param type="unsigned int" name="frames" desc="" /> <Param type="unsigned int" name="frames" desc="" />
</Callback> </Callback>
</Callbacks> </Callbacks>
<Functions count="599"> <Functions count="600">
<Function name="InitWindow" retType="void" paramCount="3" desc="Initialize window and OpenGL context"> <Function name="InitWindow" retType="void" paramCount="3" desc="Initialize window and OpenGL context">
<Param type="int" name="width" desc="" /> <Param type="int" name="width" desc="" />
<Param type="int" name="height" desc="" /> <Param type="int" name="height" desc="" />
@ -1122,10 +1122,10 @@
<Function name="IsFileNameValid" retType="bool" paramCount="1" desc="Check if fileName is valid for the platform/OS"> <Function name="IsFileNameValid" retType="bool" paramCount="1" desc="Check if fileName is valid for the platform/OS">
<Param type="const char *" name="fileName" desc="" /> <Param type="const char *" name="fileName" desc="" />
</Function> </Function>
<Function name="LoadDirectoryFiles" retType="FilePathList" paramCount="1" desc="Load directory filepaths"> <Function name="LoadDirectoryFiles" retType="FilePathList" paramCount="1" desc="Load directory filepaths, files and directories, no subdirs scan">
<Param type="const char *" name="dirPath" desc="" /> <Param type="const char *" name="dirPath" desc="" />
</Function> </Function>
<Function name="LoadDirectoryFilesEx" retType="FilePathList" paramCount="3" desc="Load directory filepaths with extension filtering and recursive directory scan. Use 'DIR' in the filter string to include directories in the result"> <Function name="LoadDirectoryFilesEx" retType="FilePathList" paramCount="3" desc="Load directory filepaths with extension filtering and subdir scan; some filters available: "*.*", "FILES*", "DIRS*"">
<Param type="const char *" name="basePath" desc="" /> <Param type="const char *" name="basePath" desc="" />
<Param type="const char *" name="filter" desc="" /> <Param type="const char *" name="filter" desc="" />
<Param type="bool" name="scanSubdirs" desc="" /> <Param type="bool" name="scanSubdirs" desc="" />
@ -1809,7 +1809,7 @@
<Param type="Image" name="image" desc="" /> <Param type="Image" name="image" desc="" />
<Param type="const char *" name="fileName" desc="" /> <Param type="const char *" name="fileName" desc="" />
</Function> </Function>
<Function name="ExportImageToMemory" retType="unsigned char *" paramCount="3" desc="Export image to memory buffer"> <Function name="ExportImageToMemory" retType="unsigned char *" paramCount="3" desc="Export image to memory buffer, memory must be MemFree()">
<Param type="Image" name="image" desc="" /> <Param type="Image" name="image" desc="" />
<Param type="const char *" name="fileType" desc="" /> <Param type="const char *" name="fileType" desc="" />
<Param type="int *" name="fileSize" desc="" /> <Param type="int *" name="fileSize" desc="" />
@ -2525,18 +2525,34 @@
<Param type="const char *" name="begin" desc="" /> <Param type="const char *" name="begin" desc="" />
<Param type="const char *" name="end" desc="" /> <Param type="const char *" name="end" desc="" />
</Function> </Function>
<Function name="TextReplace" retType="char *" paramCount="3" desc="Replace text string (WARNING: memory must be freed!)"> <Function name="TextReplace" retType="char *" paramCount="3" desc="Replace text string with new string">
<Param type="const char *" name="text" desc="" /> <Param type="const char *" name="text" desc="" />
<Param type="const char *" name="search" desc="" /> <Param type="const char *" name="search" desc="" />
<Param type="const char *" name="replacement" desc="" /> <Param type="const char *" name="replacement" desc="" />
</Function> </Function>
<Function name="TextReplaceBetween" retType="char *" paramCount="4" desc="Replace text between two specific strings (WARNING: memory must be freed!)"> <Function name="TextReplaceAlloc" retType="char *" paramCount="3" desc="Replace text string with new string, memory must be MemFree()">
<Param type="const char *" name="text" desc="" />
<Param type="const char *" name="search" desc="" />
<Param type="const char *" name="replacement" desc="" />
</Function>
<Function name="TextReplaceBetween" retType="char *" paramCount="4" desc="Replace text between two specific strings">
<Param type="const char *" name="text" desc="" /> <Param type="const char *" name="text" desc="" />
<Param type="const char *" name="begin" desc="" /> <Param type="const char *" name="begin" desc="" />
<Param type="const char *" name="end" desc="" /> <Param type="const char *" name="end" desc="" />
<Param type="const char *" name="replacement" desc="" /> <Param type="const char *" name="replacement" desc="" />
</Function> </Function>
<Function name="TextInsert" retType="char *" paramCount="3" desc="Insert text in a position (WARNING: memory must be freed!)"> <Function name="TextReplaceBetweenAlloc" retType="char *" paramCount="4" desc="Replace text between two specific strings, memory must be MemFree()">
<Param type="const char *" name="text" desc="" />
<Param type="const char *" name="begin" desc="" />
<Param type="const char *" name="end" desc="" />
<Param type="const char *" name="replacement" desc="" />
</Function>
<Function name="TextInsert" retType="char *" paramCount="3" desc="Insert text in a defined byte position">
<Param type="const char *" name="text" desc="" />
<Param type="const char *" name="insert" desc="" />
<Param type="int" name="position" desc="" />
</Function>
<Function name="TextInsertAlloc" retType="char *" paramCount="3" desc="Insert text in a defined byte position, memory must be MemFree()">
<Param type="const char *" name="text" desc="" /> <Param type="const char *" name="text" desc="" />
<Param type="const char *" name="insert" desc="" /> <Param type="const char *" name="insert" desc="" />
<Param type="int" name="position" desc="" /> <Param type="int" name="position" desc="" />
@ -2755,20 +2771,6 @@
<Param type="Vector3" name="scale" desc="" /> <Param type="Vector3" name="scale" desc="" />
<Param type="Color" name="tint" desc="" /> <Param type="Color" name="tint" desc="" />
</Function> </Function>
<Function name="DrawModelPoints" retType="void" paramCount="4" desc="Draw a model as points">
<Param type="Model" name="model" desc="" />
<Param type="Vector3" name="position" desc="" />
<Param type="float" name="scale" desc="" />
<Param type="Color" name="tint" desc="" />
</Function>
<Function name="DrawModelPointsEx" retType="void" paramCount="6" desc="Draw a model as points with extended parameters">
<Param type="Model" name="model" desc="" />
<Param type="Vector3" name="position" desc="" />
<Param type="Vector3" name="rotationAxis" desc="" />
<Param type="float" name="rotationAngle" desc="" />
<Param type="Vector3" name="scale" desc="" />
<Param type="Color" name="tint" desc="" />
</Function>
<Function name="DrawBoundingBox" retType="void" paramCount="2" desc="Draw bounding box (wires)"> <Function name="DrawBoundingBox" retType="void" paramCount="2" desc="Draw bounding box (wires)">
<Param type="BoundingBox" name="box" desc="" /> <Param type="BoundingBox" name="box" desc="" />
<Param type="Color" name="color" desc="" /> <Param type="Color" name="color" desc="" />