From 8a7aff509dd13315930884bc8374a44b6b1ead28 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 27 Mar 2026 21:56:29 +0100 Subject: [PATCH] WARNING: BREAKING: REDESIGNED: `TextInsert()`, `TextReplace()`, `TextReplaceBetween()`, using static buffers Redesign has been conditioned by the usage of those functions on `rexm`, `rpc` and other tools; for many use cases a static buffer is enough and way more comfortable to use. ADDED: `TextInsertAlloc()`, `TextReplaceAlloc()`, `TextReplaceBetweenAlloc()`, alternatives with internal allocations --- CHANGELOG | 28 +++++++--- src/raylib.h | 13 +++-- src/rcore.c | 2 +- src/rtext.c | 139 ++++++++++++++++++++++++++++++++++++++++++++-- tools/rexm/rexm.c | 53 ++++++++---------- 5 files changed, 185 insertions(+), 50 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 053ff3018..cf9bf238d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -21,11 +21,17 @@ KEY 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: 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: `ComputeSHA256()` (#5264) by @iamahuman1395 -[rcore] ADDED: `GetKeyName()` (#4544) by @lecongsebastien [rcore] REMOVED: GIF recording option, added example by @raysan5 -WARNING- [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 @@ -111,7 +117,7 @@ Detailed changes: [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: 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: Forward declare the windows stuff, prevents failures in GCC (#5269) by @Jeffm2501 [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: `rlGetPixelDataSize()`, correct compressed data size calculation per blocks #5416 by @raysan5 [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: Shader loading API function names for more consistency #5631 by @raysan5 -WARNING- [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: `ImageBlurGaussian()`, fix integer overflow in cast (#5037) by @garrisonhh [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] 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 @@ -263,11 +276,10 @@ Detailed changes: [rtext] REVIEWED: `GetCodepointCount()`, misuse of cast (#4741) by @sleeptightAnsiC [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()`, use static buffer, easier to use by @raysan5 -WARNING- [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: `TextReplaceBetween()` by @raysan5 +[rtext] REVIEWED: `TextReplace()` and `TextLength()`, avoid using `strcpy()` by @raysan5 [rtext] REVIEWED: `TextSubtext(), fixes (#4759) by @veins1 [rtext] REVIEWED: `TextToPascal()`, fix issue by @raysan5 [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: `UpdateSourceMetadata()` and `TextReplaceBetween()` 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: Report issues if logs can not be loaded by @raysan5 [rexm] REVIEWED: VS project adding to solution by @raysan5 diff --git a/src/raylib.h b/src/raylib.h index 0c2a0beda..ac5bc414e 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1359,7 +1359,7 @@ RLAPI Image LoadImageFromScreen(void); 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 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 // 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) // 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 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 @@ -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 *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 *TextReplace(const char *text, const char *search, const char *replacement); // Replace text string (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 (WARNING: memory must be freed!) -RLAPI char *TextInsert(const char *text, const char *insert, int position); // Insert text in a position (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 *TextReplaceAlloc(const char *text, const char *search, const char *replacement); // Replace text string with new string, memory must be MemFree() +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 **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 diff --git a/src/rcore.c b/src/rcore.c index 87df4d5d4..3454b65ba 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2298,7 +2298,7 @@ int FileTextReplace(const char *fileName, const char *search, const char *replac if (FileExists(fileName)) { fileText = LoadFileText(fileName); - fileTextUpdated = TextReplace(fileText, search, replacement); + fileTextUpdated = TextReplaceAlloc(fileText, search, replacement); result = SaveFileText(fileName, fileTextUpdated); MemFree(fileTextUpdated); UnloadFileText(fileText); diff --git a/src/rtext.c b/src/rtext.c index 16fe8e75f..a6ca65113 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1736,8 +1736,6 @@ const char *TextRemoveSpaces(const char *text) // Get text between two strings 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 }; 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); int len = (endIndex - beginIndex - beginLen); - if (len < (MAX_TEXT_BETWEEN_SIZE - 1)) strncpy(buffer, text + beginIndex + beginLen, len); - else strncpy(buffer, text + beginIndex + beginLen, MAX_TEXT_BETWEEN_SIZE - 1); + if (len < (MAX_TEXT_BUFFER_LENGTH - 1)) strncpy(buffer, text + beginIndex + beginLen, len); + 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 // 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) +{ + static char result[MAX_TEXT_BUFFER_LENGTH] = { 0 }; + memset(result, 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 = result; // 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 result; +} + +// 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; @@ -1825,11 +1889,46 @@ char *TextReplace(const char *text, const char *search, const char *replacement) 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 result[MAX_TEXT_BUFFER_LENGTH] = { 0 }; + memset(result, 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(result, text, beginIndex + beginLen); // Copy first text part + if (replacement != NULL) strncpy(result + beginIndex + beginLen, replacement, replaceLen); // Copy replacement (if provided) + strncpy(result + beginIndex + beginLen + replaceLen, text + endIndex, textLen - endIndex); // Copy end text part + } + } + } + + return result; +} + // Replace text between two specific strings // REQUIRES: strncpy() // NOTE: If (replacement == NULL) remove "begin"[ ]"end" text // 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; @@ -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 // WARNING: Allocated memory must be manually freed char *TextInsert(const char *text, const char *insert, int position) +{ + static char result[MAX_TEXT_BUFFER_LENGTH] = { 0 }; + memset(result, 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++) result[i] = text[i]; + for (int i = position; i < insertLen + position; i++) result[i] = insert[i - position]; + for (int i = (insertLen + position); i < (textLen + insertLen); i++) result[i] = text[i]; + + result[textLen + insertLen] = '\0'; // Add EOL + } + else TRACELOG(LOG_WARNING, "Text with inserted string is longer than internal buffer, use TextInserExt()"); + } + + return result; +} + +// 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; diff --git a/tools/rexm/rexm.c b/tools/rexm/rexm.c index e4881b633..48a621115 100644 --- a/tools/rexm/rexm.c +++ b/tools/rexm/rexm.c @@ -469,12 +469,12 @@ int main(int argc, char *argv[]) int exIndex = TextFindIndex(exText, "/****************"); // Update required info with some defaults - exTextUpdated[0] = TextReplace(exText + exIndex, "", exCategory); - exTextUpdated[1] = TextReplace(exTextUpdated[0], "", exName + strlen(exCategory) + 1); - //TextReplace(newExample, "", "Ray"); - //TextReplace(newExample, "@", "@raysan5"); - //TextReplace(newExample, "", 2025); - //TextReplace(newExample, "", 2025); + exTextUpdated[0] = TextReplaceAlloc(exText + exIndex, "", exCategory); + exTextUpdated[1] = TextReplaceAlloc(exTextUpdated[0], "", exName + strlen(exCategory) + 1); + //TextReplaceAlloc(newExample, "", "Ray"); + //TextReplaceAlloc(newExample, "@", "@raysan5"); + //TextReplaceAlloc(newExample, "", 2025); + //TextReplaceAlloc(newExample, "", 2025); SaveFileText(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName), exTextUpdated[1]); for (int i = 0; i < 6; i++) { MemFree(exTextUpdated[i]); exTextUpdated[i] = NULL; } @@ -541,8 +541,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 can not be found in: %s\n", TextFormat("%s/%s", GetDirectoryPath(inFileName), resPathUpdated)); - - RL_FREE(resPathUpdated); } } else @@ -863,9 +861,7 @@ int main(int argc, char *argv[]) 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, resPathUpdated)); - RL_FREE(resPathUpdated); + FileRemove(TextFormat("%s/%s/%s", exBasePath, exCategory, TextReplace(resPaths[r], "glsl%i", TextFormat("glsl%i", glslVer[v])))); } } else FileRemove(TextFormat("%s/%s/%s", exBasePath, exCategory, resPaths[r])); @@ -1164,7 +1160,6 @@ int main(int argc, char *argv[]) // Logging missing resources for convenience LOG("WARNING: [%s] Missing resource: %s\n", exInfo->name, resPathUpdated); } - RL_FREE(resPathUpdated); } } else @@ -1577,13 +1572,13 @@ int main(int argc, char *argv[]) " SaveFileText(\"outputLogFileName\", logText);\n" " emscripten_run_script(\"saveFileFromMEMFSToDisk('outputLogFileName','outputLogFileName')\");\n\n" " return 0"; - char *returnReplaceTextUpdated = TextReplace(returnReplaceText, "outputLogFileName", TextFormat("%s.log", exName)); + char *returnReplaceTextUpdated = TextReplacEx(returnReplaceText, "outputLogFileName", TextFormat("%s.log", exName)); char *srcTextUpdated[4] = { 0 }; - srcTextUpdated[0] = TextReplace(srcText, "int main(void)\n{", mainReplaceText); - srcTextUpdated[1] = TextReplace(srcTextUpdated[0], "WindowShouldClose()", "WindowShouldClose() && (testFramesCount < requestedTestFrames)"); - srcTextUpdated[2] = TextReplace(srcTextUpdated[1], "EndDrawing();", "EndDrawing(); testFramesCount++;"); - srcTextUpdated[3] = TextReplace(srcTextUpdated[2], " return 0", returnReplaceTextUpdated); + srcTextUpdated[0] = TextReplacEx(srcText, "int main(void)\n{", mainReplaceText); + srcTextUpdated[1] = TextReplacEx(srcTextUpdated[0], "WindowShouldClose()", "WindowShouldClose() && (testFramesCount < requestedTestFrames)"); + srcTextUpdated[2] = TextReplacEx(srcTextUpdated[1], "EndDrawing();", "EndDrawing(); testFramesCount++;"); + srcTextUpdated[3] = TextReplacEx(srcTextUpdated[2], " return 0", returnReplaceTextUpdated); MemFree(returnReplaceTextUpdated); UnloadFileText(srcText); @@ -1633,9 +1628,9 @@ int main(int argc, char *argv[]) " if ((argc > 1) && (argc == 3) && (strcmp(argv[1], \"--frames\") != 0)) requestedTestFrames = atoi(argv[2]);\n"; char *srcTextUpdated[3] = { 0 }; - srcTextUpdated[0] = TextReplace(srcText, "int main(void)\n{", mainReplaceText); - srcTextUpdated[1] = TextReplace(srcTextUpdated[0], "WindowShouldClose()", "WindowShouldClose() && (testFramesCount < requestedTestFrames)"); - srcTextUpdated[2] = TextReplace(srcTextUpdated[1], "EndDrawing();", "EndDrawing(); testFramesCount++;"); + srcTextUpdated[0] = TextReplacEx(srcText, "int main(void)\n{", mainReplaceText); + srcTextUpdated[1] = TextReplacEx(srcTextUpdated[0], "WindowShouldClose()", "WindowShouldClose() && (testFramesCount < requestedTestFrames)"); + srcTextUpdated[2] = TextReplacEx(srcTextUpdated[1], "EndDrawing();", "EndDrawing(); testFramesCount++;"); UnloadFileText(srcText); SaveFileText(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName), srcTextUpdated[2]); @@ -2042,10 +2037,8 @@ static int UpdateRequiredFiles(void) // 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); + strcpy(resPaths[r], TextReplace(resPaths[r], "glsl%i", "glsl100")); } if (r < (resPathCount - 1)) @@ -2815,7 +2808,7 @@ static void UpdateSourceMetadata(const char *exSrcPath, const rlExampleInfo *inf // Update example header title (line #3 - ALWAYS) // 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)); if (exTextUpdated[0] != NULL) exTextUpdatedPtr = exTextUpdated[0]; @@ -2829,13 +2822,13 @@ static void UpdateSourceMetadata(const char *exSrcPath, const rlExampleInfo *inf if (i < info->stars) 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)); if (exTextUpdated[1] != NULL) exTextUpdatedPtr = exTextUpdated[1]; // Update example creation/update raylib versions // 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)); if (exTextUpdated[2] != NULL) exTextUpdatedPtr = exTextUpdated[2]; @@ -2843,27 +2836,27 @@ static void UpdateSourceMetadata(const char *exSrcPath, const rlExampleInfo *inf // String: "* Copyright (c) 2019-2026 Contributor Name (@github_user) and Ramon Santamaria (@raysan5)" 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)); if (exTextUpdated[3] != NULL) exTextUpdatedPtr = exTextUpdated[3]; } 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)); if (exTextUpdated[3] != NULL) exTextUpdatedPtr = exTextUpdated[3]; } // Update window title // 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)); if (exTextUpdated[4] != NULL) exTextUpdatedPtr = exTextUpdated[4]; // Update contributors names // 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) - exTextUpdated[5] = TextReplaceBetween(exTextUpdatedPtr, "* Example contributed by ", ")", + exTextUpdated[5] = TextReplaceBetweenAlloc(exTextUpdatedPtr, "* Example contributed by ", ")", TextFormat("%s (@%s", info->author, info->authorGitHub)); if (exTextUpdated[5] != NULL) exTextUpdatedPtr = exTextUpdated[5];