13 Commits

23 changed files with 842 additions and 110 deletions
Split View
  1. +2
    -2
      .github/workflows/parse.yml
  2. +3
    -2
      .gitignore
  3. +1
    -1
      examples/README.md
  4. +7
    -5
      examples/text/text_draw_3d.c
  5. +1
    -1
      examples/textures/textures_mouse_painting.c
  6. +2
    -2
      src/config.h
  7. +1
    -1
      src/raudio.c
  8. +2
    -2
      src/rcamera.h
  9. +1
    -1
      src/rcore.c
  10. +0
    -0
      tools/parser/LICENSE
  11. +0
    -0
      tools/parser/Makefile
  12. +0
    -0
      tools/parser/README.md
  13. +0
    -0
      tools/parser/output/raylib_api.json
  14. +0
    -0
      tools/parser/output/raylib_api.lua
  15. +0
    -0
      tools/parser/output/raylib_api.txt
  16. +0
    -0
      tools/parser/output/raylib_api.xml
  17. +0
    -0
      tools/parser/raylib_parser.c
  18. +16
    -0
      tools/rexm/LICENSE
  19. +366
    -0
      tools/rexm/Makefile
  20. +20
    -0
      tools/rexm/README.md
  21. +393
    -93
      tools/rexm/rexm.c
  22. BIN
      tools/rexm/rexm.ico
  23. +27
    -0
      tools/rexm/rexm.rc

+ 2
- 2
.github/workflows/parse.yml View File

@ -1,4 +1,4 @@
name: Parse raylib_api
name: Parse raylib API
on:
workflow_dispatch:
@ -14,7 +14,7 @@ jobs:
- uses: actions/checkout@v4
- name: Update parse files
working-directory: parser
working-directory: tools/parser
run: |
make raylib_api
mv raylib_api.* output

+ 3
- 2
.gitignore View File

@ -111,5 +111,6 @@ build/
build-*/
docgen_tmp/
# Parser stuff
parser/raylib_parser
# Tools stuff
tools/parser/raylib_parser
tools/rexm/VS2022

+ 1
- 1
examples/README.md View File

@ -16,7 +16,7 @@ You may find it easier to use than other toolchains, especially when it comes to
- `zig build [module]` to compile all examples for a module (e.g. `zig build core`)
- `zig build [example]` to compile _and run_ a particular example (e.g. `zig build core_basic_window`)
## EXAMPLES LIST
## EXAMPLES COLLECTION [TOTAL: 158]
### category: core

+ 7
- 5
examples/text/text_draw_3d.c View File

@ -34,8 +34,11 @@
#include <stddef.h> // Required for: NULL
#include <math.h> // Required for: sinf()
// To make it work with the older RLGL module just comment the line below
#define RAYLIB_NEW_RLGL
#if defined(PLATFORM_DESKTOP)
#define GLSL_VERSION 330
#else // PLATFORM_ANDROID, PLATFORM_WEB
#define GLSL_VERSION 100
#endif
//--------------------------------------------------------------------------------------
// Globals
@ -50,7 +53,6 @@ bool SHOW_TEXT_BOUNDRY = false;
//--------------------------------------------------------------------------------------
// Data Types definition
//--------------------------------------------------------------------------------------
// Configuration structure for waving the text
typedef struct WaveTextConfig {
Vector3 waveRange;
@ -66,7 +68,7 @@ static void DrawTextCodepoint3D(Font font, int codepoint, Vector3 position, floa
// Draw a 2D text in 3D space
static void DrawText3D(Font font, const char *text, Vector3 position, float fontSize, float fontSpacing, float lineSpacing, bool backface, Color tint);
// Draw a 2D text in 3D space and wave the parts that start with `~~` and end with `~~`.
// Draw a 2D text in 3D space and wave the parts that start with '~~' and end with '~~'
// This is a modified version of the original code by @Nighten found here https://github.com/NightenDushi/Raylib_DrawTextStyle
static void DrawTextWave3D(Font font, const char *text, Vector3 position, float fontSize, float fontSpacing, float lineSpacing, bool backface, WaveTextConfig *config, float time, Color tint);
// Measure a text in 3D ignoring the `~~` chars.
@ -128,7 +130,7 @@ int main(void)
Color dark = RED;
// Load the alpha discard shader
Shader alphaDiscard = LoadShader(NULL, "resources/shaders/glsl330/alpha_discard.fs");
Shader alphaDiscard = LoadShader(NULL, n">TextFormat("resources/shaders/glsl%i/alpha_discard.fs", GLSL_VERSION));
// Array filled with multiple random colors (when multicolor mode is set)
Color multi[TEXT_MAX_LAYERS] = {0};

+ 1
- 1
examples/textures/textures_mouse_painting.c View File

@ -210,7 +210,7 @@ int main(void)
{
DrawRectangle(0, 0, GetScreenWidth(), GetScreenHeight(), Fade(RAYWHITE, 0.8f));
DrawRectangle(0, 150, GetScreenWidth(), 80, BLACK);
DrawText("IMAGE SAVED: my_amazing_texture_painting.png", 150, 180, 20, RAYWHITE);
DrawText("IMAGE SAVED!", 150, 180, 20, RAYWHITE);
}
EndDrawing();

+ 2
- 2
src/config.h View File

@ -135,8 +135,8 @@
#define RL_MAX_SHADER_LOCATIONS 32 // Maximum number of shader locations supported
#define RL_CULL_DISTANCE_NEAR 0.001 // Default projection matrix near cull distance
#define RL_CULL_DISTANCE_FAR 10000.0 // Default projection matrix far cull distance
#define RL_CULL_DISTANCE_NEAR 0.05 // Default projection matrix near cull distance
#define RL_CULL_DISTANCE_FAR 4000.0 // Default projection matrix far cull distance
// Default shader vertex attribute locations
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION 0

+ 1
- 1
src/raudio.c View File

@ -2087,7 +2087,7 @@ float GetMusicTimePlayed(Music music)
int framesInFirstBuffer = music.stream.buffer->isSubBufferProcessed[0]? 0 : subBufferSize;
int framesInSecondBuffer = music.stream.buffer->isSubBufferProcessed[1]? 0 : subBufferSize;
int framesInBuffers = framesInFirstBuffer + framesInSecondBuffer;
if (framesInBuffers > music.frameCount) {
if (p">(unsigned int)framesInBuffers > music.frameCount) {
if (!music.looping) framesInBuffers = music.frameCount;
}
int framesSentToMix = music.stream.buffer->frameCursorPos%subBufferSize;

+ 2
- 2
src/rcamera.h View File

@ -65,8 +65,8 @@
#endif
#if defined(RCAMERA_STANDALONE)
#define CAMERA_CULL_DISTANCE_NEAR 0.01
#define CAMERA_CULL_DISTANCE_FAR 1000.0
#define CAMERA_CULL_DISTANCE_NEAR 0.05
#define CAMERA_CULL_DISTANCE_FAR 4000.0
#else
#define CAMERA_CULL_DISTANCE_NEAR RL_CULL_DISTANCE_NEAR
#define CAMERA_CULL_DISTANCE_FAR RL_CULL_DISTANCE_FAR

+ 1
- 1
src/rcore.c View File

@ -1530,7 +1530,7 @@ Ray GetScreenToWorldRayEx(Vector2 position, Camera camera, int width, int height
double right = top*aspect;
// Calculate projection matrix from orthographic
matProj = MatrixOrtho(-right, right, -top, top, mf">0.01, 1000.0);
matProj = MatrixOrtho(-right, right, -top, top, n">rlGetCullDistanceNear(), rlGetCullDistanceFar());
}
// Unproject far/near points

parser/LICENSE → tools/parser/LICENSE View File


parser/Makefile → tools/parser/Makefile View File


parser/README.md → tools/parser/README.md View File


parser/output/raylib_api.json → tools/parser/output/raylib_api.json View File


parser/output/raylib_api.lua → tools/parser/output/raylib_api.lua View File


parser/output/raylib_api.txt → tools/parser/output/raylib_api.txt View File


parser/output/raylib_api.xml → tools/parser/output/raylib_api.xml View File


parser/raylib_parser.c → tools/parser/raylib_parser.c View File


+ 16
- 0
tools/rexm/LICENSE View File

@ -0,0 +1,16 @@
Copyright (c) 2025 Ramon Santamaria (@raysan5)
This software is provided "as-is", without any express or implied warranty. In no event
will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose, including commercial
applications, and to alter it and redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim that you
wrote the original software. If you use this software in a product, an acknowledgment
in the product documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be misrepresented
as being the original software.
3. This notice may not be removed or altered from any source distribution.

+ 366
- 0
tools/rexm/Makefile View File

@ -0,0 +1,366 @@
#**************************************************************************************************
#
# raylib makefile for Desktop platforms, Web (Wasm), Raspberry Pi (DRM mode) and Android
#
# Copyright (c) 2013-2025 Ramon Santamaria (@raysan5)
#
# This software is provided "as-is", without any express or implied warranty. In no event
# will the authors be held liable for any damages arising from the use of this software.
#
# Permission is granted to anyone to use this software for any purpose, including commercial
# applications, and to alter it and redistribute it freely, subject to the following restrictions:
#
# 1. The origin of this software must not be misrepresented; you must not claim that you
# wrote the original software. If you use this software in a product, an acknowledgment
# in the product documentation would be appreciated but is not required.
#
# 2. Altered source versions must be plainly marked as such, and must not be misrepresented
# as being the original software.
#
# 3. This notice may not be removed or altered from any source distribution.
#
#**************************************************************************************************
.PHONY: all clean
# Define required environment variables
#------------------------------------------------------------------------------------------------
# Define target platform: PLATFORM_DESKTOP, PLATFORM_WEB, PLATFORM_DRM, PLATFORM_ANDROID
PLATFORM ?= PLATFORM_DESKTOP
# Define project variables
PROJECT_NAME ?= rexm
PROJECT_VERSION ?= 1.0
PROJECT_BUILD_PATH ?= .
PROJECT_SOURCE_FILES ?= rexm.c
# raylib library variables
RAYLIB_SRC_PATH ?= C:\raylib\raylib\src
RAYLIB_INCLUDE_PATH ?= $(RAYLIB_SRC_PATH)
RAYLIB_LIB_PATH ?= $(RAYLIB_SRC_PATH)
# Library type used for raylib: STATIC (.a) or SHARED (.so/.dll)
RAYLIB_LIBTYPE ?= STATIC
# Define compiler path on Windows
COMPILER_PATH ?= C:\raylib\w64devkit\bin
# Build mode for project: DEBUG or RELEASE
BUILD_MODE ?= RELEASE
# PLATFORM_WEB: Default properties
BUILD_WEB_ASYNCIFY ?= FALSE
BUILD_WEB_SHELL ?= minshell.html
BUILD_WEB_HEAP_SIZE ?= 128MB
BUILD_WEB_STACK_SIZE ?= 1MB
BUILD_WEB_ASYNCIFY_STACK_SIZE ?= 1048576
BUILD_WEB_RESOURCES ?= FALSE
BUILD_WEB_RESOURCES_PATH ?= resources
# Determine PLATFORM_OS in case PLATFORM_DESKTOP selected
ifeq ($(PLATFORM),PLATFORM_DESKTOP)
# No uname.exe on MinGW!, but OS=Windows_NT on Windows!
# ifeq ($(UNAME),Msys) -> Windows
ifeq ($(OS),Windows_NT)
PLATFORM_OS = WINDOWS
export PATH := $(COMPILER_PATH):$(PATH)
else
UNAMEOS = $(shell uname)
ifeq ($(UNAMEOS),Linux)
PLATFORM_OS = LINUX
endif
ifeq ($(UNAMEOS),FreeBSD)
PLATFORM_OS = BSD
endif
ifeq ($(UNAMEOS),OpenBSD)
PLATFORM_OS = BSD
endif
ifeq ($(UNAMEOS),NetBSD)
PLATFORM_OS = BSD
endif
ifeq ($(UNAMEOS),DragonFly)
PLATFORM_OS = BSD
endif
ifeq ($(UNAMEOS),Darwin)
PLATFORM_OS = OSX
endif
endif
endif
ifeq ($(PLATFORM),PLATFORM_DRM)
UNAMEOS = $(shell uname)
ifeq ($(UNAMEOS),Linux)
PLATFORM_OS = LINUX
endif
endif
ifeq ($(PLATFORM_OS),WINDOWS)
ifeq ($(PLATFORM),PLATFORM_WEB)
# Emscripten required variables
EMSDK_PATH ?= C:/raylib/emsdk
EMSCRIPTEN_PATH ?= $(EMSDK_PATH)/upstream/emscripten
CLANG_PATH = $(EMSDK_PATH)/upstream/bin
PYTHON_PATH = $(EMSDK_PATH)/python/3.9.2-nuget_64bit
NODE_PATH = $(EMSDK_PATH)/node/20.18.0_64bit/bin
export PATH = $(EMSDK_PATH);$(EMSCRIPTEN_PATH);$(CLANG_PATH);$(NODE_PATH);$(PYTHON_PATH):$$(PATH)
endif
endif
# Define default C compiler: CC
#------------------------------------------------------------------------------------------------
CC = gcc
ifeq ($(PLATFORM),PLATFORM_DESKTOP)
ifeq ($(PLATFORM_OS),OSX)
# OSX default compiler
CC = clang
endif
ifeq ($(PLATFORM_OS),BSD)
# FreeBSD, OpenBSD, NetBSD, DragonFly default compiler
CC = clang
endif
endif
ifeq ($(PLATFORM),PLATFORM_WEB)
# HTML5 emscripten compiler
# WARNING: To compile to HTML5, code must be redesigned
# to use emscripten.h and emscripten_set_main_loop()
CC = emcc
endif
ifeq ($(PLATFORM),PLATFORM_DRM)
ifeq ($(USE_RPI_CROSS_COMPILER),TRUE)
# Define RPI cross-compiler
#CC = armv6j-hardfloat-linux-gnueabi-gcc
CC = $(RPI_TOOLCHAIN)/bin/arm-linux-gnueabihf-gcc
endif
endif
# Define default make program: MAKE
#------------------------------------------------------------------------------------------------
MAKE ?= make
ifeq ($(PLATFORM),PLATFORM_DESKTOP)
ifeq ($(PLATFORM_OS),WINDOWS)
MAKE = mingw32-make
endif
endif
# Define compiler flags: CFLAGS
#------------------------------------------------------------------------------------------------
# -O1 defines optimization level
# -g include debug information on compilation
# -s strip unnecessary data from build
# -Wall turns on most, but not all, compiler warnings
# -std=c99 defines C language mode (standard C from 1999 revision)
# -std=gnu99 defines C language mode (GNU C from 1999 revision)
# -Wno-missing-braces ignore invalid warning (GCC bug 53119)
# -Wno-unused-value ignore unused return values of some functions (i.e. fread())
# -D_DEFAULT_SOURCE use with -std=c99 on Linux and PLATFORM_WEB, required for timespec
CFLAGS = -std=c99 -Wall -Wno-missing-braces -Wno-unused-value -Wno-pointer-sign -D_DEFAULT_SOURCE $(PROJECT_CUSTOM_FLAGS)
#CFLAGS += -Wextra -Wmissing-prototypes -Wstrict-prototypes
ifeq ($(BUILD_MODE),DEBUG)
CFLAGS += -g -D_DEBUG
else
ifeq ($(PLATFORM),PLATFORM_WEB)
ifeq ($(BUILD_WEB_ASYNCIFY),TRUE)
CFLAGS += -O3
else
CFLAGS += -Os
endif
else
ifeq ($(PLATFORM_OS),OSX)
CFLAGS += -O2
else
CFLAGS += -s -O2
endif
endif
endif
ifeq ($(PLATFORM),PLATFORM_DRM)
CFLAGS += -std=gnu99 -DEGL_NO_X11
endif
# Define include paths for required headers: INCLUDE_PATHS
#------------------------------------------------------------------------------------------------
INCLUDE_PATHS += -I. -Iexternal -I$(RAYLIB_INCLUDE_PATH)
# Define additional directories containing required header files
ifeq ($(PLATFORM),PLATFORM_DRM)
# DRM required libraries
INCLUDE_PATHS += -I/usr/include/libdrm
endif
ifeq ($(PLATFORM),PLATFORM_DESKTOP)
ifeq ($(PLATFORM_OS),BSD)
# Consider -L$(RAYLIB_H_INSTALL_PATH)
INCLUDE_PATHS += -I/usr/local/include
endif
endif
# Define library paths containing required libs: LDFLAGS
#------------------------------------------------------------------------------------------------
LDFLAGS = -L. -L$(RAYLIB_LIB_PATH)
ifeq ($(PLATFORM),PLATFORM_DESKTOP)
ifeq ($(PLATFORM_OS),WINDOWS)
# NOTE: The resource .rc file contains windows executable icon and properties
LDFLAGS += $(PROJECT_NAME).rc.data
# -Wl,--subsystem,windows hides the console window
ifeq ($(BUILD_MODE), RELEASE)
LDFLAGS += -Wl,--subsystem,windows
endif
endif
ifeq ($(PLATFORM_OS),BSD)
# Consider -L$(RAYLIB_INSTALL_PATH)
LDFLAGS += -Lsrc -L/usr/local/lib
endif
ifeq ($(PLATFORM_OS),LINUX)
# Reset everything.
# Precedence: immediately local, installed version, raysan5 provided libs
#LDFLAGS += -L$(RAYLIB_RELEASE_PATH)
endif
endif
ifeq ($(PLATFORM),PLATFORM_WEB)
# -Os # size optimization
# -O2 # optimization level 2, if used, also set --memory-init-file 0
# -sUSE_GLFW=3 # Use glfw3 library (context/input management)
# -sALLOW_MEMORY_GROWTH=1 # to allow memory resizing -> WARNING: Audio buffers could FAIL!
# -sTOTAL_MEMORY=16777216 # to specify heap memory size (default = 16MB) (67108864 = 64MB)
# -sUSE_PTHREADS=1 # multithreading support
# -sWASM=0 # disable Web Assembly, emitted by default
# -sASYNCIFY # lets synchronous C/C++ code interact with asynchronous JS
# -sFORCE_FILESYSTEM=1 # force filesystem to load/save files data
# -sASSERTIONS=1 # enable runtime checks for common memory allocation errors (-O1 and above turn it off)
# -sMINIFY_HTML=0 # minify generated html from shell.html
# --profiling # include information for code profiling
# --memory-init-file 0 # to avoid an external memory initialization code file (.mem)
# --preload-file resources # specify a resources folder for data compilation
# --source-map-base # allow debugging in browser with source map
# --shell-file shell.html # define a custom shell .html and output extension
LDFLAGS += -sUSE_GLFW=3 -sTOTAL_MEMORY=$(BUILD_WEB_HEAP_SIZE) -sSTACK_SIZE=$(BUILD_WEB_STACK_SIZE) -sFORCE_FILESYSTEM=1 -sMINIFY_HTML=0
# Build using asyncify
ifeq ($(BUILD_WEB_ASYNCIFY),TRUE)
LDFLAGS += -sASYNCIFY -sASYNCIFY_STACK_SIZE=$(BUILD_WEB_ASYNCIFY_STACK_SIZE)
endif
# Add resources building if required
ifeq ($(BUILD_WEB_RESOURCES),TRUE)
LDFLAGS += --preload-file $(BUILD_WEB_RESOURCES_PATH)
endif
# Add debug mode flags if required
ifeq ($(BUILD_MODE),DEBUG)
LDFLAGS += -sASSERTIONS=1 --profiling
endif
# Define a custom shell .html and output extension
LDFLAGS += --shell-file $(BUILD_WEB_SHELL)
EXT = .html
endif
# Define libraries required on linking: LDLIBS
# NOTE: To link libraries (lib<name>.so or lib<name>.a), use -l<name>
#------------------------------------------------------------------------------------------------
ifeq ($(PLATFORM),PLATFORM_DESKTOP)
ifeq ($(PLATFORM_OS),WINDOWS)
# Libraries for Windows desktop compilation
# NOTE: WinMM library required to set high-res timer resolution
LDLIBS = -lraylib -lopengl32 -lgdi32 -lwinmm -lcomdlg32 -lole32
# Required for physac examples
LDLIBS += -static -lpthread
endif
ifeq ($(PLATFORM_OS),LINUX)
# Libraries for Debian GNU/Linux desktop compiling
# NOTE: Required packages: libegl1-mesa-dev
LDLIBS = -lraylib -lGL -lm -lpthread -ldl -lrt
# On Wayland windowing system, additional libraries requires
ifeq ($(USE_WAYLAND_DISPLAY),TRUE)
LDLIBS += -lwayland-client -lwayland-cursor -lwayland-egl -lxkbcommon
else
# On X11 requires also below libraries
LDLIBS += -lX11
# NOTE: It seems additional libraries are not required any more, latest GLFW just dlopen them
#LDLIBS += -lXrandr -lXinerama -lXi -lXxf86vm -lXcursor
endif
# Explicit link to libc
ifeq ($(RAYLIB_LIBTYPE),SHARED)
LDLIBS += -lc
endif
endif
ifeq ($(PLATFORM_OS),OSX)
# Libraries for OSX 10.9 desktop compiling
# NOTE: Required packages: libopenal-dev libegl1-mesa-dev
LDLIBS = -lraylib -framework OpenGL -framework Cocoa -framework IOKit -framework CoreAudio -framework CoreVideo
endif
ifeq ($(PLATFORM_OS),BSD)
# Libraries for FreeBSD, OpenBSD, NetBSD, DragonFly desktop compiling
# NOTE: Required packages: mesa-libs
LDLIBS = -lraylib -lGL -lpthread -lm
# On XWindow requires also below libraries
LDLIBS += -lX11 -lXrandr -lXinerama -lXi -lXxf86vm -lXcursor
endif
endif
ifeq ($(PLATFORM),PLATFORM_WEB)
# Libraries for web (HTML5) compiling
LDLIBS = $(RAYLIB_LIB_PATH)/libraylib.a
endif
ifeq ($(PLATFORM),PLATFORM_DRM)
# Libraries for DRM compiling
# NOTE: Required packages: libasound2-dev (ALSA)
LDLIBS = -lraylib -lGLESv2 -lEGL -lpthread -lrt -lm -lgbm -ldrm -ldl
endif
# Define all object files from source files
#------------------------------------------------------------------------------------------------
OBJS = $(patsubst %.c, %.o, $(PROJECT_SOURCE_FILES))
# Define processes to execute
#------------------------------------------------------------------------------------------------
# For Android platform we call a custom Makefile.Android
ifeq ($(PLATFORM),PLATFORM_ANDROID)
MAKEFILE_TARGET = -f Makefile.Android
export PROJECT_NAME
export PROJECT_SOURCE_FILES
else
MAKEFILE_TARGET = $(PROJECT_NAME)
endif
# Default target entry
# NOTE: We call this Makefile target or Makefile.Android target
all:
$(MAKE) $(MAKEFILE_TARGET)
# Project target defined by PROJECT_NAME
$(PROJECT_NAME): $(OBJS)
$(CC) -o $(PROJECT_BUILD_PATH)/$(PROJECT_NAME)$(EXT) $(OBJS) $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM)
# Compile source files
# NOTE: This pattern will compile every module defined on $(OBJS)
%.o: %.c
$(CC) -c $< -o $@ $(CFLAGS) $(INCLUDE_PATHS) -D$(PLATFORM)
# Clean everything
clean:
ifeq ($(PLATFORM),PLATFORM_DESKTOP)
ifeq ($(PLATFORM_OS),WINDOWS)
del *.o *.exe /s
endif
ifeq ($(PLATFORM_OS),LINUX)
find . -type f -executable -delete
rm -fv *.o
endif
ifeq ($(PLATFORM_OS),OSX)
rm -f *.o external/*.o $(PROJECT_NAME)
endif
endif
ifeq ($(PLATFORM),PLATFORM_DRM)
find . -type f -executable -delete
rm -fv *.o
endif
ifeq ($(PLATFORM),PLATFORM_WEB)
del *.o *.html *.js
endif
@echo Cleaning done

+ 20
- 0
tools/rexm/README.md View File

@ -0,0 +1,20 @@
## rexm
### Description
raylib examples manager
### Features
- $(Project Feature 01)
- $(Project Feature 02)
- $(Project Feature 03)
### Command line
### License
This project sources are licensed under an unmodified zlib/libpng license, which is an OSI-certified, BSD-like license that allows static linking with closed source software. Check [LICENSE](LICENSE) for further details.
*Copyright (c) 2025 Ramon Santamaria (@raysan5)*

examples/rexm.c → tools/rexm/rexm.c View File

@ -58,6 +58,10 @@
#define LOG(...)
#endif
#define REXM_MAX_BUFFER_SIZE (2*1024*1024) // 2MB
#define REXM_MAX_RESOURCE_PATHS 256
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
@ -92,7 +96,10 @@ static const char *exBasePath = "C:/GitHub/raylib/examples";
static const char *exWebPath = "C:/GitHub/raylib.com/examples";
static const char *exTemplateFilePath = "C:/GitHub/raylib/examples/examples_template.c";
static const char *exTemplateScreenshot = "C:/GitHub/raylib/examples/examples_template.png";
static const char *exCollectionListPath = "C:/GitHub/raylib/examples/examples_list.txt";
static const char *exCollectionFilePath = "C:/GitHub/raylib/examples/examples_list.txt";
//const char *exBasePath = getenv("REXM_EXAMPLES_PATH");
//if (!exBasePath) exBasePath = "default/path";
//----------------------------------------------------------------------------------
// Module specific functions declaration
@ -101,6 +108,7 @@ static int FileTextReplace(const char *fileName, const char *textLookUp, const c
static int FileCopy(const char *srcPath, const char *dstPath);
static int FileRename(const char *fileName, const char *fileRename);
static int FileRemove(const char *fileName);
static int FileMove(const char *srcPath, const char *dstPath);
// Update required files from examples collection
// UPDATES: Makefile, Makefile.Web, README.md, examples.js
@ -125,6 +133,12 @@ static int ParseExampleInfoLine(const char *line, rlExampleInfo *entry);
// WARNING: items[] pointers are reorganized
static void SortExampleByName(rlExampleInfo *items, int count);
// Scan resource paths in example file
static char **ScanExampleResources(const char *filePath, int *resPathCount);
// Clear resource paths scanned
static void ClearExampleResources(char **resPaths);
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
@ -226,7 +240,7 @@ int main(int argc, char *argv[])
else
{
// Verify example exists in collection to be removed
char *exColInfo = LoadFileText(exCollectionListPath);
char *exColInfo = LoadFileText(exCollectionFilePath);
if (TextFindIndex(exColInfo, argv[2]) != -1) // Example in the collection
{
strcpy(exName, argv[2]); // Register example name
@ -247,7 +261,7 @@ int main(int argc, char *argv[])
else
{
// Verify example exists in collection to be removed
char *exColInfo = LoadFileText(exCollectionListPath);
char *exColInfo = LoadFileText(exCollectionFilePath);
if (TextFindIndex(exColInfo, argv[2]) != -1) // Example in the collection
{
strcpy(exName, argv[2]); // Register filename for removal
@ -296,22 +310,73 @@ int main(int argc, char *argv[])
// Create: raylib/examples/<category>/<category>_example_name.png
FileCopy(exTemplateScreenshot, TextFormat("%s/%s/%s.png", exBasePath, exCategory, exName)); // WARNING: To be updated manually!
// Copy: raylib/examples/<category>/resources/... // WARNING: To be updated manually!
// IDEA: Example to be added could be provided as a .zip, containing resources!
// TODO: Copy provided resources to respective directories
// Possible strategy:
// 1. Scan code file for resources paths -> Resources list
// Look for specific text: '.png"'
// Look for full path, previous '"'
// Be careful with shaders: '.vs"', '.fs"' -> Reconstruct path manually?
// 2. Verify paths: resource files exist
// 3. Copy files to required resource dir
// Copy: raylib/examples/<category>/resources/...
// -----------------------------------------------------------------------------------------
// Scan resources used in example to copy
int resPathCount = 0;
char **resPaths = ScanExampleResources(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName), &resPathCount);
if (resPathCount > 0)
{
for (int r = 0; r < resPathCount; r++)
{
// WARNING: Special case to consider: shaders, resource paths could use conditions: "glsl%i"
// In this case, multiple resources are required: glsl100, glsl120, glsl330
if (TextFindIndex(resPaths[r], "glsl%i") > -1)
{
int glslVer[3] = { 100, 120, 330 };
for (int v = 0; v < 3; v++)
{
char *resPathUpdated = TextReplace(resPaths[r], "glsl%i", TextFormat("glsl%i", glslVer[v]));
LOG("INFO: Example resource required: %s\n", resPathUpdated);
if (FileExists(TextFormat("%s/%s", GetDirectoryPath(inFileName), resPathUpdated)))
{
// Verify the resources are placed in "resources" directory
if (TextFindIndex(resPathUpdated, "resources/") > 0)
{
// NOTE: Look for resources in the path of the provided .c to be added
// To be copied to <category>/resources directory, extra dirs are automatically created if required
FileCopy(TextFormat("%s/%s", GetDirectoryPath(inFileName), resPathUpdated),
TextFormat("%s/%s/%s", exBasePath, exCategory, resPathUpdated));
}
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));
RL_FREE(resPathUpdated);
}
}
else
{
LOG("INFO: Example resource required: %s\n", resPaths[r]);
if (FileExists(TextFormat("%s/%s", GetDirectoryPath(inFileName), resPaths[r])))
{
// Verify the resources are placed in "resources" directory
if (TextFindIndex(resPaths[r], "resources/") > 0)
{
// NOTE: Look for resources in the path of the provided .c to be added
// To be copied to <category>/resources directory, extra dirs are automatically created if required
FileCopy(TextFormat("%s/%s", GetDirectoryPath(inFileName), resPaths[r]),
TextFormat("%s/%s/%s", exBasePath, exCategory, resPaths[r]));
}
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), resPaths[r]));
}
}
}
ClearExampleResources(resPaths);
// -----------------------------------------------------------------------------------------
// Add example to the collection list, if not already there
// NOTE: Required format: shapes;shapes_basic_shapes;;1.0;4.2;"Ray";@raysan5
//------------------------------------------------------------------------------------------------
char *exColInfo = LoadFileText(exCollectionListPath);
char *exColInfo = LoadFileText(exCollectionFilePath);
if (TextFindIndex(exColInfo, exName) == -1) // Example not found
{
char *exColInfoUpdated = (char *)RL_CALLOC(2*1024*1024, 1); // Updated list copy, 2MB
@ -348,7 +413,7 @@ int main(int argc, char *argv[])
memcpy(exColInfoUpdated + catIndex + textWritenSize, exColInfo + catIndex, strlen(exColInfo) - catIndex);
}
SaveFileText(exCollectionListPath, exColInfoUpdated);
SaveFileText(exCollectionFilePath, exColInfoUpdated);
RL_FREE(exColInfoUpdated);
}
else LOG("WARNING: ADD: Example is already on the collection\n");
@ -403,42 +468,49 @@ int main(int argc, char *argv[])
if (strcmp(exCategory, exRecategory) == 0)
{
// Rename example on collection
FileTextReplace(exCollectionListPath, TextFormat("%s;%s", exCategory, exName),
FileTextReplace(exCollectionFilePath, TextFormat("%s;%s", exCategory, exName),
TextFormat("%s;%s", exRecategory, exRename));
// ">Rename all required files
// l">Edit: Rename example code and screenshot files .c and .png
rename(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName),
TextFormat("%s/%s/%s.c", exBasePath, exCategory, exRename));
rename(TextFormat("%s/%s/%s.png", exBasePath, exCategory, exName),
TextFormat("%s/%s/%s.png", exBasePath, exCategory, exRename));
// Rename example on required files
// NOTE: Example resource files do not need to be changed...
// unless the example is moved from one caegory to another
// Edit: Rename example on required files
FileTextReplace(TextFormat("%s/Makefile", exBasePath), exName, exRename);
FileTextReplace(TextFormat("%s/Makefile.Web", exBasePath), exName, exRename);
FileTextReplace(TextFormat("%s/README.md", exBasePath), exName, exRename);
FileTextReplace(TextFormat("%s/../common/examples.js", exWebPath), exName, exRename);
// Rename example project and solution
// l">Edit: Rename example project and solution
rename(TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exName),
TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exRename));
FileTextReplace(TextFormat("%s/../projects/VS2022/raylib.sln", exBasePath), exName, exRename);
}
else
{
// Rename with change of category
// TODO: Reorder collection as required
FileTextReplace(exCollectionListPath, TextFormat("%s;%s", exCategory, exName),
// l">WARNING: Rename with change of category
// TODO: Reorder collection to place renamed example at the end of category
FileTextReplace(exCollectionFilePath, TextFormat("%s;%s", exCategory, exName),
TextFormat("%s;%s", exRecategory, exRename));
// Rename all required files
// TODO: Move example resources from <src_category>/resources to <dst_category>/resources
// WARNING: Resources can be shared with other examples in the category
// Edit: Rename example code file (copy and remove)
FileCopy(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName),
TextFormat("%s/%s/%s.c", exBasePath, exCategory, exRename));
remove(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName));
// Edit: Rename example screenshot file (copy and remove)
FileCopy(TextFormat("%s/%s/%s.png", exBasePath, exCategory, exName),
TextFormat("%s/%s/%s.png", exBasePath, exCategory, exRename));
remove(TextFormat("%s/%s/%s.png", exBasePath, exCategory, exName));
// Edit: Update required files: Makefile, Makefile.Web, README.md, examples.js
UpdateRequiredFiles();
}
@ -468,7 +540,7 @@ int main(int argc, char *argv[])
{
// Remove example from collection for files update
//------------------------------------------------------------------------------------------------
char *exColInfo = LoadFileText(exCollectionListPath);
char *exColInfo = LoadFileText(exCollectionFilePath);
int exIndex = TextFindIndex(exColInfo, TextFormat("%s;%s", exCategory, exName));
if (exIndex > 0) // Example found
{
@ -480,21 +552,53 @@ int main(int argc, char *argv[])
// Remove line and copy the rest next
memcpy(exColInfoUpdated + exIndex, exColInfo + exIndex + lineLen + 1, strlen(exColInfo) - exIndex - lineLen);
SaveFileText(exCollectionListPath, exColInfoUpdated);
SaveFileText(exCollectionFilePath, exColInfoUpdated);
RL_FREE(exColInfoUpdated);
}
else LOG("WARNING: REMOVE: Example not found in the collection\n");
UnloadFileText(exColInfo);
//------------------------------------------------------------------------------------------------
// Remove: raylib/examples/<category>/resources/..
// WARNING: Some of those resources could be used by other examples,
// just leave this process to manual update for now!
// -----------------------------------------------------------------------------------------
/*
// Scan resources used in example to be removed
int resPathCount = 0;
char **resPaths = ScanExampleResources(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName), &resPathCount);
if (resPathCount > 0)
{
for (int r = 0; r < resPathCount; r++)
{
// WARNING: Special case to consider: shaders, resource paths could use conditions: "glsl%i"
// In this case, multiple resources are required: glsl100, glsl120, glsl330
if (TextFindIndex(resPaths[r], "glsl%i") > -1)
{
int glslVer[3] = { 100, 120, 330 };
for (int v = 0; v < 3; v++)
{
char *resPathUpdated = TextReplace(resPaths[r], "glsl%i", TextFormat("glsl%i", glslVer[v]));
remove(TextFormat("%s/%s/%s", exBasePath, exCategory, resPathUpdated));
RL_FREE(resPathUpdated);
}
}
else remove(TextFormat("%s/%s/%s", exBasePath, exCategory, resPaths[r]));
}
}
ClearExampleResources(resPaths);
*/
// -----------------------------------------------------------------------------------------
// Remove: raylib/examples/<category>/<category>_example_name.c
// Remove: raylib/examples/<category>/<category>_example_name.png
remove(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName));
remove(TextFormat("%s/%s/%s.png", exBasePath, exCategory, exName));
// TODO: Remove: raylib/examples/<category>/resources/..
// Get list of resources from Makefile.Web or examples ResourcesScan()
// Edit: Update required files: Makefile, Makefile.Web, README.md, examples.js
UpdateRequiredFiles();
// Remove: raylib/projects/VS2022/examples/<category>_example_name.vcxproj
@ -518,21 +622,29 @@ int main(int argc, char *argv[])
} break;
case 5: // Validate
{
// TODO: Validate examples collection against [examples_list.txt]
// Validate: raylib/examples/<category>/<category>_example_name.c
// Validate: raylib/examples/<category>/<category>_example_name.png
// Validate: raylib/examples/<category>/resources/.. -> Not possible for now...
// Validate: raylib/examples/Makefile
// Validate: raylib/examples/Makefile.Web
// Validate: raylib/examples/README.md
// Validate: raylib/projects/VS2022/examples/<category>_example_name.vcxproj
// Validate: raylib/projects/VS2022/raylib.sln
// Validate: raylib.com/common/examples.js
// Validate: raylib.com/examples/<category>/<category>_example_name.html
// Validate: raylib.com/examples/<category>/<category>_example_name.data
// Validate: raylib.com/examples/<category>/<category>_example_name.wasm
// Validate: raylib.com/examples/<category>/<category>_example_name.js
// TODO: Validate examples in collection list [examples_list.txt] -> Source of truth!
// Validate: raylib/examples/<category>/<category>_example_name.c -> File exists?
// Validate: raylib/examples/<category>/<category>_example_name.png -> File exists?
// Validate: raylib/examples/<category>/resources/.. -> Example resources available?
// Validate: raylib/examples/Makefile -> Example listed?
// Validate: raylib/examples/Makefile.Web -> Example listed?
// Validate: raylib/examples/README.md -> Example listed?
// Validate: raylib/projects/VS2022/examples/<category>_example_name.vcxproj -> File exists?
// Validate: raylib/projects/VS2022/raylib.sln -> Example listed?
// Validate: raylib.com/common/examples.js -> Example listed?
// Validate: raylib.com/examples/<category>/<category>_example_name.html -> File exists?
// Validate: raylib.com/examples/<category>/<category>_example_name.data -> File exists?
// Validate: raylib.com/examples/<category>/<category>_example_name.wasm -> File exists?
// Validate: raylib.com/examples/<category>/<category>_example_name.js -> File exists?
// Additional validation elements
// Validate: Example naming conventions: <category>/<category>_example_name
// Validate: Duplicate entries in collection list
// Validate: Example info (stars, author, github) missmatches with example content
// After validation, update required files for consistency
// Update files: Makefile, Makefile.Web, README.md, examples.js
UpdateRequiredFiles();
} break;
default: // Help
@ -599,13 +711,13 @@ static int UpdateRequiredFiles(void)
{
mkIndex += sprintf(mkTextUpdated + mkListStartIndex + mkIndex, TextFormat("%s = \\\n", TextToUpper(exCategories[i])));
int exCount = 0;
rlExampleInfo *exCatList = LoadExamplesData(exCollectionListPath, exCategories[i], true, &exCount);
int exCollectionCount = 0;
rlExampleInfo *exCollection = LoadExamplesData(exCollectionFilePath, exCategories[i], true, &exCollectionCount);
for (int x = 0; x < exCount - 1; x++) mkIndex += sprintf(mkTextUpdated + mkListStartIndex + mkIndex, TextFormat(" %s/%s \\\n", exCatList[x].category, exCatList[x].name));
mkIndex += sprintf(mkTextUpdated + mkListStartIndex + mkIndex, TextFormat(" %s/%s\n\n", exCatList[exCount - 1].category, exCatList[exCount - 1].name));
for (int x = 0; x < exCollectionCount - 1; x++) mkIndex += sprintf(mkTextUpdated + mkListStartIndex + mkIndex, TextFormat(" %s/%s \\\n", exCollection[x].category, exCollection[x].name));
mkIndex += sprintf(mkTextUpdated + mkListStartIndex + mkIndex, TextFormat(" %s/%s\n\n", exCollection[exCollectionCount - 1].category, exCollection[exCollectionCount - 1].name));
UnloadExamplesData(exCatList);
UnloadExamplesData(exCollection);
}
// Add the remaining part of the original file
@ -618,6 +730,7 @@ static int UpdateRequiredFiles(void)
//------------------------------------------------------------------------------------------------
// Edit: raylib/examples/Makefile.Web --> Update from collection
// NOTE: We avoid the "others" category on web building
//------------------------------------------------------------------------------------------------
char *mkwText = LoadFileText(TextFormat("%s/Makefile.Web", exBasePath));
char *mkwTextUpdated = (char *)RL_CALLOC(2*1024*1024, 1); // Updated Makefile copy, 2MB
@ -629,24 +742,106 @@ static int UpdateRequiredFiles(void)
memcpy(mkwTextUpdated, mkwText, mkwListStartIndex);
mkwIndex = sprintf(mkwTextUpdated + mkwListStartIndex, "#EXAMPLES_LIST_START\n");
for (int i = 0; i < MAX_EXAMPLE_CATEGORIES; i++)
// NOTE: We avoid the "others" category on web building
for (int i = 0; i < MAX_EXAMPLE_CATEGORIES - 1; i++)
{
mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, TextFormat("%s = \\\n", TextToUpper(exCategories[i])));
int exCount = 0;
rlExampleInfo *exCatList = LoadExamplesData(exCollectionListPath, exCategories[i], true, &exCount);
int exCollectionCount = 0;
rlExampleInfo *exCollection = LoadExamplesData(exCollectionFilePath, exCategories[i], true, &exCollectionCount);
for (int x = 0; x < exCount - 1; x++) mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, TextFormat(" %s/%s \\\n", exCatList[x].category, exCatList[x].name));
mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, TextFormat(" %s/%s\n\n", exCatList[exCount - 1].category, exCatList[exCount - 1].name));
for (int x = 0; x < exCollectionCount - 1; x++) mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, TextFormat(" %s/%s \\\n", exCollection[x].category, exCollection[x].name));
mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, TextFormat(" %s/%s\n\n", exCollection[exCollectionCount - 1].category, exCollection[exCollectionCount - 1].name));
UnloadExamplesData(exCatList);
UnloadExamplesData(exCollection);
}
// Add examples individual targets, considering every example resources
// Some required makefile code...
mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, "# Default target entry\n");
mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, "all: $(CORE) $(SHAPES) $(TEXT) $(TEXTURES) $(MODELS) $(SHADERS) $(AUDIO)\n\n");
mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, "core: $(CORE)\n");
mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, "shapes: $(SHAPES)\n");
mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, "textures: $(TEXTURES)\n");
mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, "text: $(TEXT)\n");
mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, "models: $(MODELS)\n");
mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, "shaders: $(SHADERS)\n");
mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, "audio: $(AUDIO)\n\n");
// NOTE: We avoid the "others" category on web building
for (int i = 0; i < MAX_EXAMPLE_CATEGORIES - 1; i++)
{
mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, TextFormat("# Compile %s examples\n", TextToUpper(exCategories[i])));
int exCollectionCount = 0;
rlExampleInfo *exCollection = LoadExamplesData(exCollectionFilePath, exCategories[i], true, &exCollectionCount);
for (int x = 0; x < exCollectionCount; x++)
{
// Scan resources used in example to list
int resPathCount = 0;
char **resPaths = ScanExampleResources(TextFormat("%s/%s/%s.c", exBasePath, exCollection[x].category, exCollection[x].name), &resPathCount);
if (resPathCount > 0)
{
/*
// WARNING: Compilation line starts with [TAB]
shaders/shaders_vertex_displacement: shaders/shaders_vertex_displacement.c
$(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \
--preload-file shaders/resources/shaders/glsl100/vertex_displacement.vs@resources/shaders/glsl100/vertex_displacement.vs \
--preload-file shaders/resources/shaders/glsl330/vertex_displacement.vs@resources/shaders/glsl330/vertex_displacement.vs \
--preload-file shaders/resources/shaders/glsl100/vertex_displacement.fs@resources/shaders/glsl100/vertex_displacement.fs \
--preload-file shaders/resources/shaders/glsl330/vertex_displacement.fs@resources/shaders/glsl330/vertex_displacement.fs
*/
mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex,
TextFormat("%s/%s: %s/%s.c\n", exCollection[x].category, exCollection[x].name, exCollection[x].category, exCollection[x].name));
mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, " $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \\\n");
for (int r = 0; r < resPathCount; r++)
{
// WARNING: Special case to consider: shaders, resource paths could use conditions: "glsl%i"
// In this case, we focus on web building for: glsl100
if (TextFindIndex(resPaths[r], "glsl%i") > -1)
{
char *resPathUpdated = TextReplace(resPaths[r], "glsl%i", "glsl100");
memset(resPaths[r], 0, 256);
strcpy(resPaths[r], resPathUpdated);
RL_FREE(resPathUpdated);
}
if (r < (resPathCount - 1))
{
mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex,
TextFormat(" --preload-file %s/%s@%s \\\n", exCollection[x].category, resPaths[r], resPaths[r]));
}
else
{
mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex,
TextFormat(" --preload-file %s/%s@%s\n\n", exCollection[x].category, resPaths[r], resPaths[r]));
}
}
}
else // Example does not require resources
{
/*
// WARNING: Compilation line starts with [TAB]
core/core_2d_camera: core/core_2d_camera.c
$(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM)
*/
mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex,
TextFormat("%s/%s: %s/%s.c\n", exCollection[x].category, exCollection[x].name, exCollection[x].category, exCollection[x].name));
mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, " $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM)\n\n");
}
ClearExampleResources(resPaths);
}
UnloadExamplesData(exCollection);
}
// Add the remaining part of the original file
memcpy(mkwTextUpdated + mkwListStartIndex + mkwIndex - 1, mkwText + mkwListEndIndex, strlen(mkwText) - mkwListEndIndex);
// TODO: Add new example target, considering resources
// Save updated file
SaveFileText(TextFormat("%s/Makefile.Web", exBasePath), mkwTextUpdated);
UnloadFileText(mkwText);
@ -660,87 +855,100 @@ static int UpdateRequiredFiles(void)
char *mdText = LoadFileText(TextFormat("%s/README.md", exBasePath));
char *mdTextUpdated = (char *)RL_CALLOC(2*1024*1024, 1); // Updated examples.js copy, 2MB
int mdListStartIndex = TextFindIndex(mdText, "| 01 | ");
int mdListStartIndex = TextFindIndex(mdText, "## EXAMPLES COLLECTION");
int mdIndex = 0;
memcpy(mdTextUpdated, mdText, mdListStartIndex);
int exCollectionFullCount = 0;
rlExampleInfo *exCollectionFull = LoadExamplesData(exCollectionFilePath, "ALL", false, &exCollectionFullCount);
UnloadExamplesData(exCollectionFull);
mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, TextFormat("## EXAMPLES COLLECTION [TOTAL: %i]\n\n", exCollectionFullCount));
// NOTE: We keep a global examples counter
for (int i = 0, catCount = 0, gCount = 0; i < MAX_EXAMPLE_CATEGORIES; i++)
for (int i = 0; i < MAX_EXAMPLE_CATEGORIES; i++)
{
int exCollectionCount = 0;
rlExampleInfo *exCollection = LoadExamplesData(exCollectionFilePath, exCategories[i], false, &exCollectionCount);
// Every category includes some introductory text, as it is quite short, just copying it here
// NOTE: "core" text already placed in the file
if (i == 1) // "shapes"
if (i == 0) // "core"
{
mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, TextFormat("\n### category: core [%i]\n\n", exCollectionCount));
mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex,
"Examples using raylib[core](../src/rcore.c) platform functionality like window creation, inputs, drawing modes and system functionality.\n\n");
}
else if (i == 1) // "shapes"
{
mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "\n### category: shapes\n\n");
mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, n">TextFormat("\n### category: shapes [%i]\n\n", exCollectionCount));
mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex,
"Examples using raylib shapes drawing functionality, provided by raylib [shapes](../src/rshapes.c) module.\n\n");
}
else if (i == 2) // "textures"
{
mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "\n### category: textures\n\n");
mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, n">TextFormat("\n### category: textures [%i]\n\n", exCollectionCount));
mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex,
"Examples using raylib textures functionality, including image/textures loading/generation and drawing, provided by raylib [textures](../src/rtextures.c) module.\n\n");
}
else if (i == 3) // "text"
{
mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "\n### category: text\n\n");
mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, n">TextFormat("\n### category: text [%i]\n\n", exCollectionCount));
mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex,
"Examples using raylib text functionality, including sprite fonts loading/generation and text drawing, provided by raylib [text](../src/rtext.c) module.\n\n");
}
else if (i == 4) // "models"
{
mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "\n### category: models\n\n");
mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, n">TextFormat("\n### category: models [%i]\n\n", exCollectionCount));
mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex,
"Examples using raylib models functionality, including models loading/generation and drawing, provided by raylib [models](../src/rmodels.c) module.\n\n");
}
else if (i == 5) // "shaders"
{
mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "\n### category: shaders\n\n");
mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, n">TextFormat("\n### category: shaders [%i]\n\n", exCollectionCount));
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");
}
else if (i == 6) // "audio"
{
mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "\n### category: audio\n\n");
mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, n">TextFormat("\n### category: audio [%i]\n\n", exCollectionCount));
mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex,
"Examples using raylib audio functionality, including sound/music loading and playing. This functionality is provided by raylib [raudio](../src/raudio.c) module. Note this module can be used standalone independently of raylib.\n\n");
}
else if (i == 7) // "others"
{
mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "\n### category: others\n\n");
mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, n">TextFormat("\n### category: others [%i]\n\n", exCollectionCount));
mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex,
"Examples showing raylib misc functionality that does not fit in other categories, like standalone modules usage or examples integrating external libraries.\n\n");
}
if (i > 0)
{
// Table header required
mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "| ## | example | image | difficulty<br>level | version<br>created | last version<br>updated | original<br>developer |\n");
mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "|----|----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------|\n");
}
// Table header required
mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "| example | image | difficulty<br>level | version<br>created | last version<br>updated | original<br>developer |\n");
mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "|-----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------|\n");
rlExampleInfo *exCatList = LoadExamplesData(exCollectionListPath, exCategories[i], false, &catCount);
for (int x = 0; x < catCount; x++)
for (int x = 0; x < exCollectionCount; x++)
{
char stars[16] = { 0 };
for (int s = 0; s < 4; s++)
{
if (s < exCatList[x].stars) strcpy(stars + 3*s, "⭐️");
if (s < exCollection[x].stars) strcpy(stars + 3*s, "⭐️");
else strcpy(stars + 3*s, "");
}
mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex,
TextFormat("| %02i | [%s](%s/%s.c) | <img src=\"%s/%s.png\" alt=\"%s\" width=\"80\"> | %s | %.1f | %.1f | [%s](https://github.com/%s) |\n",
gCount + 1, exCatList[x].name, exCatList[x].category, exCatList[x].name, exCatList[x].category, exCatList[x].name, exCatList[x].name,
stars, exCatList[x].verCreated, exCatList[x].verUpdated, exCatList[x].author, exCatList[x].authorGitHub + 1));
gCount++;
TextFormat("| [%s](%s/%s.c) | <img src=\"%s/%s.png\" alt=\"%s\" width=\"80\"> | %s | %.1f | %.1f | [%s](https://github.com/%s) |\n",
exCollection[x].name, exCollection[x].category, exCollection[x].name, exCollection[x].category, exCollection[x].name, exCollection[x].name,
stars, exCollection[x].verCreated, exCollection[x].verUpdated, exCollection[x].author, exCollection[x].authorGitHub + 1));
}
UnloadExamplesData(exCatList);
UnloadExamplesData(exCollection);
}
mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex,
"\nSome example missing? As always, contributions are welcome, feel free to send new examples!\n");
mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex,
"Here is an[examples template](examples_template.c) with instructions to start with!\n");
// Save updated file
SaveFileText(TextFormat("%s/README.md", exBasePath), mdTextUpdated);
UnloadFileText(mdText);
@ -762,32 +970,33 @@ static int UpdateRequiredFiles(void)
jsIndex += sprintf(jsTextUpdated + jsListStartIndex + jsIndex, " var exampleData = [\n");
// NOTE: We avoid "others" category
for (int i = 0, exCount = 0; i < MAX_EXAMPLE_CATEGORIES - 1; i++)
for (int i = 0; i < MAX_EXAMPLE_CATEGORIES - 1; i++)
{
rlExampleInfo *exCatList = LoadExamplesData(exCollectionListPath, exCategories[i], false, &exCount);
for (int x = 0; x < exCount; x++)
int exCollectionCount = 0;
rlExampleInfo *exCollection = LoadExamplesData(exCollectionFilePath, exCategories[i], false, &exCollectionCount);
for (int x = 0; x < exCollectionCount; x++)
{
char stars[16] = { 0 };
for (int s = 0; s < 4; s++)
{
if (s < exCatList[x].stars) strcpy(stars + 3*s, "⭐️");
if (s < exCollection[x].stars) strcpy(stars + 3*s, "⭐️");
else strcpy(stars + 3*s, "");
}
if ((i == 6) && (x == (exCount - 1)))
if ((i == 6) && (x == (exCollectionCount - 1)))
{
// NOTE: Last line to add, special case to consider
jsIndex += sprintf(jsTextUpdated + jsListStartIndex + jsIndex,
TextFormat(" exampleEntry('%s', '%s', '%s')];\n", stars, exCatList[x].category, exCatList[x].name + strlen(exCatList[x].category) + 1));
TextFormat(" exampleEntry('%s', '%s', '%s')];\n", stars, exCollection[x].category, exCollection[x].name + strlen(exCollection[x].category) + 1));
}
else
{
jsIndex += sprintf(jsTextUpdated + jsListStartIndex + jsIndex,
TextFormat(" exampleEntry('%s', '%s', '%s'),\n", stars, exCatList[x].category, exCatList[x].name + strlen(exCatList[x].category) + 1));
TextFormat(" exampleEntry('%s', '%s', '%s'),\n", stars, exCollection[x].category, exCollection[x].name + strlen(exCollection[x].category) + 1));
}
}
UnloadExamplesData(exCatList);
UnloadExamplesData(exCollection);
}
// Add the remaining part of the original file
@ -816,7 +1025,7 @@ static rlExampleInfo *LoadExamplesData(const char *fileName, const char *categor
if (text != NULL)
{
int lineCount = 0;
">const char **lines = LoadTextLines(text, &lineCount);
char **lines = LoadTextLines(text, &lineCount);
for (int i = 0; i < lineCount; i++)
{
@ -849,6 +1058,7 @@ static rlExampleInfo *LoadExamplesData(const char *fileName, const char *categor
}
}
UnloadTextLines(lines);
UnloadFileText(text);
}
@ -925,6 +1135,21 @@ static int FileRemove(const char *fileName)
return result;
}
// Move file from one directory to another
// NOTE: If dst directories do not exists they are created
static int FileMove(const char *srcPath, const char *dstPath)
{
int result = 0;
if (FileExists(srcPath))
{
FileCopy(srcPath, dstPath);
remove(srcPath);
}
return result;
}
// Load text lines
static char **LoadTextLines(const char *text, int *count)
{
@ -1018,3 +1243,78 @@ static void SortExampleByName(rlExampleInfo *items, int count)
{
qsort(items, count, sizeof(rlExampleInfo), rlExampleInfoCompare);
}
// Scan resource paths in example file
// WARNING: Supported resource file extensions is hardcoded by used file types
// but new examples could require other file extensions to be added,
// maybe it should look for '.xxx")' patterns instead
static char **ScanExampleResources(const char *filePath, int *resPathCount)
{
#define REXM_MAX_RESOURCE_PATH_LEN 256
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));
int resCounter = 0;
char *code = LoadFileText(filePath);
if (code != NULL)
{
// Resources extensions to check
const char *exts[] = { ".png", ".bmp", ".jpg", ".qoi", ".gif", ".raw", ".hdr", ".ttf", ".fnt", ".wav", ".ogg", ".mp3", ".flac", ".mod", ".qoa", ".qoa", ".obj", ".iqm", ".glb", ".m3d", ".vox", ".vs", ".fs", ".txt" };
const int extCount = sizeof(exts)/sizeof(char *);
char *ptr = code;
while ((ptr = strchr(ptr, '"')) != NULL)
{
char *start = ptr + 1;
char *end = strchr(start, '"');
if (!end) break;
int len = end - start;
if ((len > 0) && (len < REXM_MAX_RESOURCE_PATH_LEN))
{
char buffer[REXM_MAX_RESOURCE_PATH_LEN] = { 0 };
strncpy(buffer, start, len);
buffer[len] = '\0';
// Check for known extensions
for (int i = 0; i < extCount; i++)
{
if (IsFileExtension(buffer, exts[i]))
{
// Avoid duplicates
bool found = false;
for (int j = 0; j < resCounter; j++)
{
if (TextIsEqual(paths[j], buffer)) { found = true; break; }
}
if (!found && (resCounter < REXM_MAX_RESOURCE_PATHS))
{
strcpy(paths[resCounter], buffer);
resCounter++;
}
break;
}
}
}
ptr = end + 1;
}
UnloadFileText(code);
}
*resPathCount = resCounter;
return paths;
}
// Clear resource paths scanned
static void ClearExampleResources(char **resPaths)
{
for (int i = 0; i < REXM_MAX_RESOURCE_PATHS; i++) RL_FREE(resPaths[i]);
RL_FREE(resPaths);
}

BIN
tools/rexm/rexm.ico View File

Before After

+ 27
- 0
tools/rexm/rexm.rc View File

@ -0,0 +1,27 @@
GLFW_ICON ICON "rexm.ico"
1 VERSIONINFO
FILEVERSION 1,0,0,0
PRODUCTVERSION 1,0,0,0
BEGIN
BLOCK "StringFileInfo"
BEGIN
//BLOCK "080904E4" // English UK
BLOCK "040904E4" // English US
BEGIN
VALUE "CompanyName", "Ramon Santamaria"
VALUE "FileDescription", "rexm | raylib examples manager"
VALUE "FileVersion", "1.0"
VALUE "InternalName", "rexm"
VALUE "LegalCopyright", "(c) 2025 Ramon Santamaria"
//VALUE "OriginalFilename", "rexm.exe"
VALUE "rexm", "rexm"
VALUE "ProductVersion", "1.0"
END
END
BLOCK "VarFileInfo"
BEGIN
//VALUE "Translation", 0x809, 1252 // English UK
VALUE "Translation", 0x409, 1252 // English US
END
END

Loading…
Cancel
Save