From 70cb8d15e0b1a22ab62f36d88a17e8d82b816cc6 Mon Sep 17 00:00:00 2001 From: dan-hoang <56205882+dan-hoang@users.noreply.github.com> Date: Fri, 13 Mar 2026 10:51:49 -0700 Subject: [PATCH 1/3] [examples] Add `audio_stream_callback` (#5638) * Add audio_raw_stream_callback.c * Update audio_raw_stream_callback.c to more closely follow raylib coding conventions * Remove spaces before asterisks in comments in audio_raw_stream_callback.c * Put SetTargetFPS(30) before while(!WindowShouldClose()) in audio_raw_stream_callback.c * Add audio_stream_callback.c * Delete examples/audio/audio_raw_stream_callback.c * Delete examples/audio/audio_raw_stream_callback.png * Update audio_raw_stream.c to more closely follow raylib coding conventions Drawing code wasn't tabbed in * Remove screenshot code in audio_stream_callback.c * Update raylib version and copyright year in audio_raw_stream.c * Update audio_stream_callback.c Used if instead of switch to compact code; seemed more readable in this case. --- examples/audio/audio_raw_stream.c | 44 ++--- examples/audio/audio_stream_callback.c | 236 +++++++++++++++++++++++ examples/audio/audio_stream_callback.png | Bin 0 -> 15750 bytes 3 files changed, 258 insertions(+), 22 deletions(-) create mode 100644 examples/audio/audio_stream_callback.c create mode 100644 examples/audio/audio_stream_callback.png diff --git a/examples/audio/audio_raw_stream.c b/examples/audio/audio_raw_stream.c index 536f8c311..64c9b5a50 100644 --- a/examples/audio/audio_raw_stream.c +++ b/examples/audio/audio_raw_stream.c @@ -4,14 +4,14 @@ * * Example complexity rating: [★★★☆] 3/4 * -* Example originally created with raylib 1.6, last time updated with raylib 4.2 +* Example originally created with raylib 1.6, last time updated with raylib 6.0 * * Example created by Ramon Santamaria (@raysan5) and reviewed by James Hofmann (@triplefox) * * Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, * BSD-like license that allows static linking with closed source software * -* Copyright (c) 2015-2025 Ramon Santamaria (@raysan5) and James Hofmann (@triplefox) +* Copyright (c) 2015-2026 Ramon Santamaria (@raysan5) and James Hofmann (@triplefox) * ********************************************************************************************/ @@ -109,26 +109,26 @@ int main(void) // Draw //---------------------------------------------------------------------------------- BeginDrawing(); - ClearBackground(RAYWHITE); - - DrawText(TextFormat("sine frequency: %i", sineFrequency), screenWidth - 220, 10, 20, RED); - DrawText(TextFormat("pan: %.2f", pan), screenWidth - 220, 30, 20, RED); - DrawText("Up/down to change frequency", 10, 10, 20, DARKGRAY); - DrawText("Left/right to pan", 10, 30, 20, DARKGRAY); - - int windowStart = (GetTime() - sineStartTime)*SAMPLE_RATE; - int windowSize = 0.1f*SAMPLE_RATE; - int wavelength = SAMPLE_RATE/sineFrequency; - - // Draw a sine wave with the same frequency as the one being sent to the audio stream - for (int i = 0; i < screenWidth; i++) - { - int t0 = windowStart + i*windowSize/screenWidth; - int t1 = windowStart + (i + 1)*windowSize/screenWidth; - Vector2 startPos = { i, 250 + 50*sin(2*PI*t0/wavelength) }; - Vector2 endPos = { i + 1, 250 + 50*sin(2*PI*t1/wavelength) }; - DrawLineV(startPos, endPos, RED); - } + ClearBackground(RAYWHITE); + + DrawText(TextFormat("sine frequency: %i", sineFrequency), screenWidth - 220, 10, 20, RED); + DrawText(TextFormat("pan: %.2f", pan), screenWidth - 220, 30, 20, RED); + DrawText("Up/down to change frequency", 10, 10, 20, DARKGRAY); + DrawText("Left/right to pan", 10, 30, 20, DARKGRAY); + + int windowStart = (GetTime() - sineStartTime)*SAMPLE_RATE; + int windowSize = 0.1f*SAMPLE_RATE; + int wavelength = SAMPLE_RATE/sineFrequency; + + // Draw a sine wave with the same frequency as the one being sent to the audio stream + for (int i = 0; i < screenWidth; i++) + { + int t0 = windowStart + i*windowSize/screenWidth; + int t1 = windowStart + (i + 1)*windowSize/screenWidth; + Vector2 startPos = { i, 250 + 50*sin(2*PI*t0/wavelength) }; + Vector2 endPos = { i + 1, 250 + 50*sin(2*PI*t1/wavelength) }; + DrawLineV(startPos, endPos, RED); + } EndDrawing(); //---------------------------------------------------------------------------------- diff --git a/examples/audio/audio_stream_callback.c b/examples/audio/audio_stream_callback.c new file mode 100644 index 000000000..e31e8e80b --- /dev/null +++ b/examples/audio/audio_stream_callback.c @@ -0,0 +1,236 @@ +/******************************************************************************************* +* +* raylib [audio] example - stream callback +* +* Example complexity rating: [★★★☆] 3/4 +* +* Example originally created with raylib 6.0, last time updated with raylib 6.0 +* +* Example created by Dan Hoang (@dan-hoang) and reviewed by +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2015-2026 +* +********************************************************************************************/ + +#include "raylib.h" +#include +#include + +#define BUFFER_SIZE 4096 +#define SAMPLE_RATE 44100 + +// This example sends a wave to the audio device +// The user gets the choice of four waves: sine, square, triangle, and sawtooth +// A stream is set up to play to the audio device +// The stream is hooked to a callback that generates a wave +// The callback used is determined by user choice +typedef enum +{ + SINE, + SQUARE, + TRIANGLE, + SAWTOOTH +} WaveType; + +int waveFrequency = 440; +int newWaveFrequency = 440; +int waveIndex = 0; + +// Buffer for keeping the last second of uploaded audio, part of which will be drawn on the screen +float buffer[SAMPLE_RATE] = {}; + +void SineCallback(void *framesOut, unsigned int frameCount) +{ + int wavelength = SAMPLE_RATE/waveFrequency; + + // Synthesize the sine wave + for (int i = 0; i < frameCount; i++) + { + ((float *)framesOut)[i] = sin(2*PI*waveIndex/wavelength); + + waveIndex++; + + if (waveIndex >= wavelength) + { + waveFrequency = newWaveFrequency; + waveIndex = 0; + } + } + + // Save the synthesized samples for later drawing + for (int i = 0; i < SAMPLE_RATE - frameCount; i++) buffer[i] = buffer[i + frameCount]; + for (int i = 0; i < frameCount; i++) buffer[SAMPLE_RATE - frameCount + i] = ((float *)framesOut)[i]; +} + +void SquareCallback(void *framesOut, unsigned int frameCount) +{ + int wavelength = SAMPLE_RATE/waveFrequency; + + // Synthesize the square wave + for (int i = 0; i < frameCount; i++) + { + ((float *)framesOut)[i] = (waveIndex < wavelength/2)? 1 : -1; + waveIndex++; + + if (waveIndex >= wavelength) + { + waveFrequency = newWaveFrequency; + waveIndex = 0; + } + } + + // Save the synthesized samples for later drawing + for (int i = 0; i < SAMPLE_RATE - frameCount; i++) buffer[i] = buffer[i + frameCount]; + for (int i = 0; i < frameCount; i++) buffer[SAMPLE_RATE - frameCount + i] = ((float *)framesOut)[i]; +} + +void TriangleCallback(void *framesOut, unsigned int frameCount) +{ + int wavelength = SAMPLE_RATE/waveFrequency; + + // Synthesize the triangle wave + for (int i = 0; i < frameCount; i++) + { + ((float *)framesOut)[i] = (waveIndex < wavelength/2)? (-1 + 2.0f*waveIndex/(wavelength/2)) : (1 - 2.0f*(waveIndex - wavelength/2)/(wavelength/2)); + waveIndex++; + + if (waveIndex >= wavelength) + { + waveFrequency = newWaveFrequency; + waveIndex = 0; + } + } + + // Save the synthesized samples for later drawing + for (int i = 0; i < SAMPLE_RATE - frameCount; i++) buffer[i] = buffer[i + frameCount]; + for (int i = 0; i < frameCount; i++) buffer[SAMPLE_RATE - frameCount + i] = ((float *)framesOut)[i]; +} + +void SawtoothCallback(void *framesOut, unsigned int frameCount) +{ + int wavelength = SAMPLE_RATE/waveFrequency; + + // Synthesize the sawtooth wave + for (int i = 0; i < frameCount; i++) + { + ((float *)framesOut)[i] = -1 + 2.0f*waveIndex/wavelength; + waveIndex++; + + if (waveIndex >= wavelength) + { + waveFrequency = newWaveFrequency; + waveIndex = 0; + } + } + + // Save the synthesized samples for later drawing + for (int i = 0; i < SAMPLE_RATE - frameCount; i++) buffer[i] = buffer[i + frameCount]; + for (int i = 0; i < frameCount; i++) buffer[SAMPLE_RATE - frameCount + i] = ((float *)framesOut)[i]; +} + +AudioCallback waveCallbacks[] = { SineCallback, SquareCallback, TriangleCallback, SawtoothCallback }; +char *waveTypesAsString[] = { "sine", "square", "triangle", "sawtooth" }; + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [audio] example - stream callback"); + + InitAudioDevice(); + + // Set the number of samples the stream will keep in memory at a time to BUFFER_SIZE + SetAudioStreamBufferSizeDefault(BUFFER_SIZE); + + // Init raw audio stream (sample rate: 44100, sample size: 32bit-float, channels: 1-mono) + AudioStream stream = LoadAudioStream(SAMPLE_RATE, 32, 1); + PlayAudioStream(stream); + + // Configure it so that waveCallbacks[waveType] is called whenever stream is out of samples + WaveType waveType = SINE; + SetAudioStreamCallback(stream, waveCallbacks[waveType]); + + SetTargetFPS(30); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + + if (IsKeyDown(KEY_UP)) + { + newWaveFrequency += 10; + if (newWaveFrequency > 12500) newWaveFrequency = 12500; + } + + if (IsKeyDown(KEY_DOWN)) + { + newWaveFrequency -= 10; + if (newWaveFrequency < 20) newWaveFrequency = 20; + } + + if (IsKeyPressed(KEY_LEFT)) + { + if (waveType == SINE) waveType = SAWTOOTH; + else if (waveType == SQUARE) waveType = SINE; + else if (waveType == TRIANGLE) waveType = SQUARE; + else waveType = TRIANGLE; + + SetAudioStreamCallback(stream, waveCallbacks[waveType]); + } + + if (IsKeyPressed(KEY_RIGHT)) + { + if (waveType == SINE) waveType = SQUARE; + else if (waveType == SQUARE) waveType = TRIANGLE; + else if (waveType == TRIANGLE) waveType = SAWTOOTH; + else waveType = SINE; + + SetAudioStreamCallback(stream, waveCallbacks[waveType]); + } + + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + DrawText(TextFormat("frequency: %i", newWaveFrequency), screenWidth - 220, 10, 20, RED); + DrawText(TextFormat("wave type: %s", waveTypesAsString[waveType]), screenWidth - 220, 30, 20, RED); + DrawText("Up/down to change frequency", 10, 10, 20, DARKGRAY); + DrawText("Left/right to change wave type", 10, 30, 20, DARKGRAY); + + // Draw the last 10 ms of uploaded audio + for (int i = 0; i < screenWidth; i++) + { + Vector2 startPos = { i, 250 - 50*buffer[SAMPLE_RATE - SAMPLE_RATE/100 + i*SAMPLE_RATE/100/screenWidth] }; + Vector2 endPos = { i + 1, 250 - 50*buffer[SAMPLE_RATE - SAMPLE_RATE/100 + (i + 1)*SAMPLE_RATE/100/screenWidth] }; + DrawLineV(startPos, endPos, RED); + } + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadAudioStream(stream); // Close raw audio stream and delete buffers from RAM + CloseAudioDevice(); // Close audio device (music streaming is automatically stopped) + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} diff --git a/examples/audio/audio_stream_callback.png b/examples/audio/audio_stream_callback.png new file mode 100644 index 0000000000000000000000000000000000000000..12305744e5f06d9f1f465524f75db1dedca46753 GIT binary patch literal 15750 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 Date: Fri, 13 Mar 2026 20:53:11 +0300 Subject: [PATCH 2/3] Adapt build.zig to redesigned raylib build features config system (#5645) --- build.zig | 107 ++++++++++++++++-------------------------------------- 1 file changed, 32 insertions(+), 75 deletions(-) diff --git a/build.zig b/build.zig index 7eb46699e..3dc95bc83 100644 --- a/build.zig +++ b/build.zig @@ -90,41 +90,7 @@ fn setDesktopPlatform(raylib: *std.Build.Step.Compile, platform: PlatformBackend } } -/// A list of all flags from `src/config.h` that one may override -const config_h_flags = outer: { - // Set this value higher if compile errors happen as `src/config.h` gets larger - @setEvalBranchQuota(1 << 20); - - const config_h = @embedFile("src/config.h"); - var flags: [std.mem.count(u8, config_h, "\n") + 1][]const u8 = undefined; - - var i = 0; - var lines = std.mem.tokenizeScalar(u8, config_h, '\n'); - while (lines.next()) |line| { - if (!std.mem.containsAtLeast(u8, line, 1, "SUPPORT")) continue; - if (std.mem.containsAtLeast(u8, line, 1, "MODULE")) continue; - if (std.mem.startsWith(u8, line, "//")) continue; - if (std.mem.startsWith(u8, line, "#if")) continue; - - var flag = std.mem.trimStart(u8, line, " \t"); // Trim whitespace - flag = flag["#define ".len - 1 ..]; // Remove #define - flag = std.mem.trimStart(u8, flag, " \t"); // Trim whitespace - flag = flag[0 .. std.mem.indexOf(u8, flag, " ") orelse continue]; // Flag is only one word, so capture till space - flag = "-D" ++ flag; // Prepend with -D - - flags[i] = flag; - i += 1; - } - - // Uncomment this to check what flags normally get passed - //@compileLog(flags[0..i].*); - break :outer flags[0..i].*; -}; - fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, options: Options) !*std.Build.Step.Compile { - var raylib_flags_arr: std.ArrayList([]const u8) = .empty; - defer raylib_flags_arr.deinit(b.allocator); - const raylib = b.addLibrary(.{ .name = "raylib", .linkage = options.linkage, @@ -135,29 +101,27 @@ fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std. }), }); - try raylib_flags_arr.appendSlice( + raylib.root_module.addCMacro("_GNU_SOURCE", ""); + raylib.root_module.addCMacro("GL_SILENCE_DEPRECATION", "199309L"); + + var raylib_flags_arr: std.ArrayList([]const u8) = .empty; + defer raylib_flags_arr.deinit(b.allocator); + + try raylib_flags_arr.append( b.allocator, - &[_][]const u8{ - "-std=gnu99", - "-D_GNU_SOURCE", - "-DGL_SILENCE_DEPRECATION=199309L", - "-fno-sanitize=undefined", // https://github.com/raysan5/raylib/issues/3674 - }, + "-std=gnu99", ); if (options.linkage == .dynamic) { - try raylib_flags_arr.appendSlice( + try raylib_flags_arr.append( b.allocator, - &[_][]const u8{ - "-fPIC", - "-DBUILD_LIBTYPE_SHARED", - }, + "-fPIC", ); + + raylib.root_module.addCMacro("BUILD_LIBTYPE_SHARED", ""); } if (options.config.len > 0) { - // Sets a flag indicating the use of a custom `config.h` - try raylib_flags_arr.append(b.allocator, "-DEXTERNAL_CONFIG_FLAGS"); // Splits a space-separated list of config flags into multiple flags // // Note: This means certain flags like `-x c++` won't be processed properly. @@ -165,30 +129,9 @@ fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std. var config_iter = std.mem.tokenizeScalar(u8, options.config, ' '); // Apply config flags supplied by the user - while (config_iter.next()) |config_flag| + while (config_iter.next()) |config_flag| { try raylib_flags_arr.append(b.allocator, config_flag); - - // Apply all relevant configs from `src/config.h` *except* the user-specified ones - // - // Note: Currently using a suboptimal `O(m*n)` time algorithm where: - // `m` corresponds roughly to the number of lines in `src/config.h` - // `n` corresponds to the number of user-specified flags - outer: for (config_h_flags) |flag| { - // If a user already specified the flag, skip it - config_iter.reset(); - while (config_iter.next()) |config_flag| { - // For a user-specified flag to match, it must share the same prefix and have the - // same length or be followed by an equals sign - if (!std.mem.startsWith(u8, config_flag, flag)) continue; - if (config_flag.len == flag.len or config_flag[flag.len] == '=') continue :outer; - } - - // Otherwise, append default value from config.h to compile flags - try raylib_flags_arr.append(b.allocator, flag); } - } else { - // Set default config if no custom config got set - try raylib_flags_arr.appendSlice(b.allocator, &config_h_flags); } // No GLFW required on PLATFORM_DRM @@ -200,24 +143,38 @@ fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std. c_source_files.appendSliceAssumeCapacity(&.{"src/rcore.c"}); if (options.rshapes) { + raylib.root_module.addCMacro("SUPPORT_MODULE_RSHAPES", "1"); try c_source_files.append(b.allocator, "src/rshapes.c"); - try raylib_flags_arr.append(b.allocator, "-DSUPPORT_MODULE_RSHAPES"); + } else { + raylib.root_module.addCMacro("SUPPORT_MODULE_RSHAPES", "0"); } + if (options.rtextures) { + raylib.root_module.addCMacro("SUPPORT_MODULE_RTEXTURES", "1"); try c_source_files.append(b.allocator, "src/rtextures.c"); - try raylib_flags_arr.append(b.allocator, "-DSUPPORT_MODULE_RTEXTURES"); + } else { + raylib.root_module.addCMacro("SUPPORT_MODULE_RTEXTURES", "0"); } + if (options.rtext) { + raylib.root_module.addCMacro("SUPPORT_MODULE_RTEXT", "1"); try c_source_files.append(b.allocator, "src/rtext.c"); - try raylib_flags_arr.append(b.allocator, "-DSUPPORT_MODULE_RTEXT"); + } else { + raylib.root_module.addCMacro("SUPPORT_MODULE_RTEXT", "0"); } + if (options.rmodels) { + raylib.root_module.addCMacro("SUPPORT_MODULE_RMODELS", "1"); try c_source_files.append(b.allocator, "src/rmodels.c"); - try raylib_flags_arr.append(b.allocator, "-DSUPPORT_MODULE_RMODELS"); + } else { + raylib.root_module.addCMacro("SUPPORT_MODULE_RMODELS", "0"); } + if (options.raudio) { + raylib.root_module.addCMacro("SUPPORT_MODULE_RAUDIO", "1"); try c_source_files.append(b.allocator, "src/raudio.c"); - try raylib_flags_arr.append(b.allocator, "-DSUPPORT_MODULE_RAUDIO"); + } else { + raylib.root_module.addCMacro("SUPPORT_MODULE_RAUDIO", "0"); } if (options.opengl_version != .auto) { From 38ed50c07bbf694fa9cad756b5273d8daedefc2e Mon Sep 17 00:00:00 2001 From: ghera Date: Fri, 13 Mar 2026 18:53:39 +0100 Subject: [PATCH 3/3] [rcore][android] Fix CMake shared build and improve --wrap WARNING docs (#5646) --- src/CMakeLists.txt | 2 +- src/platforms/rcore_android.c | 38 +++++++++++++++++++++-------------- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0975457ee..dbd5a62a6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -78,7 +78,7 @@ endif() if (${PLATFORM} MATCHES "Android") # Wrap fopen at link time so all code (including third-party libs) goes # through __wrap_fopen, which handles Android APK asset loading - target_link_options(raylib INTERFACE -Wl,--wrap=fopen) + target_link_options(raylib PUBLIC -Wl,--wrap=fopen) endif() set_target_properties(raylib PROPERTIES diff --git a/src/platforms/rcore_android.c b/src/platforms/rcore_android.c index 42b58ff82..4442632c4 100644 --- a/src/platforms/rcore_android.c +++ b/src/platforms/rcore_android.c @@ -276,21 +276,29 @@ static fpos_t android_seek(void *cookie, fpos_t offset, int whence); static int android_close(void *cookie); // WARNING: fopen() calls are intercepted via linker flag -Wl,--wrap=fopen: the linker renames -// the original fopen -> __real_fopen and redirects all call sites to __wrap_fopen -// The flag MUST be applied at every final link step that needs wrapping, -// it has no effect when only building a static archive (.a) +// the original fopen -> __real_fopen and redirects all call sites to __wrap_fopen. +// The flag MUST be applied at every final link step that needs wrapping; +// it has no effect when only building a static archive (.a). // -// CMake: no action required, raylib's CMakeLists.txt already sets -// target_link_options(raylib INTERFACE -Wl,--wrap=fopen) which propagates to -// the final app link, wrapping app code and all static (.a) dependencies too -// Make (SHARED): no action required for raylib itself, src/Makefile already sets -// LDFLAGS += -Wl,--wrap=fopen wrapping fopen inside libraylib.so only; -// app code and static (.a) dependencies are NOT wrapped unless -Wl,--wrap=fopen -// is also added to the final app link step -// Make (STATIC): pass -Wl,--wrap=fopen to the linker command producing the final artifact -// build.zig: no dedicated wrap helper; pass -Wl,--wrap=fopen to the linker command producing -// the final artifact -// custom: pass -Wl,--wrap=fopen to the linker command producing the final artifact +// STATIC library (.a) — wrapping deferred to consumer's final link step: +// both raylib and consumer fopen calls are wrapped together in one link +// CMake: handled automatically — the PUBLIC flag propagates as INTERFACE_LINK_OPTIONS +// to the consumer's final link via target_link_libraries +// Make: pass -Wl,--wrap=fopen to the linker command producing the final artifact +// build.zig: pass -Wl,--wrap=fopen to the linker command producing the final artifact +// custom: pass -Wl,--wrap=fopen to the linker command producing the final artifact +// +// SHARED library (.so) — wrapping is self-contained: +// only fopen calls linked into the .so are wrapped; the consumer's own fopen calls +// are NOT wrapped unless the consumer also links with -Wl,--wrap=fopen independently +// CMake: handled automatically — CMakeLists.txt sets target_link_options(raylib PUBLIC +// -Wl,--wrap=fopen) which applies the flag to the .so link; +// only raylib internals are wrapped, app code requires a separate flag +// Make: handled automatically — src/Makefile sets LDFLAGS += -Wl,--wrap=fopen; +// only raylib internals are wrapped, app code requires a separate flag +// build.zig: NOT supported — std.Build has no dedicated linker wrap helper, the flag +// is not correctly applied at the .so link step +// custom: apply -Wl,--wrap=fopen to the linker command producing the .so FILE *__real_fopen(const char *fileName, const char *mode); // Real fopen, provided by the linker (--wrap=fopen) FILE *__wrap_fopen(const char *fileName, const char *mode); // Replacement for fopen() @@ -1542,7 +1550,7 @@ static void SetupFramebuffer(int width, int height) // Replacement for fopen(), used as linker wrap entry point (-Wl,--wrap=fopen) // REF: https://developer.android.com/ndk/reference/group/asset -FILE *__wrap_fopen(const char *fileName, const char *mode) +__attribute__((visibility("default"))) FILE *__wrap_fopen(const char *fileName, const char *mode) { FILE *file = NULL;