5 Commits

Author SHA1 Message Date
8fa5f1fe2c [examples] Fixed shaders_game_of_life for web (#5399)
* [examples] Fixed `shaders_game_of_life` for web

* Fixed image loadig for rexm
2025-12-11 13:38:08 +01:00
6f7cd3a9ab [core] Camera2d comment updates (#5401)
* Make the comments on the camera 2d fields more clear about what space each one is in.

* rlparser: update raylib_api.* by CI

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-12-11 13:37:17 +01:00
ae438e804e rlparser: update raylib_api.* by CI 2025-12-11 12:00:15 +00:00
Ray
2a566544d4 ADDED: Multiply security checks to avoid crashes on wrongly provided string data #4751
- REVIEWED: Checking `NULL` input on functions getting `const char *text`, to avoid crashes
- REVIEWED: `strcpy()` usage, prioritize `strncpy()` with limited copy to buffer size
- REPLACED: `strlen()` by `TextLength()` on [rtext] module
- REVIEWED: Replaced some early returns (but keeping others, for easier code following)
2025-12-11 12:59:55 +01:00
Ray
71a35f661e Update rexm.c 2025-12-11 12:33:05 +01:00
15 changed files with 343 additions and 285 deletions

View File

@ -1367,8 +1367,15 @@ shaders/shaders_fog_rendering: shaders/shaders_fog_rendering.c
shaders/shaders_game_of_life: shaders/shaders_game_of_life.c shaders/shaders_game_of_life: shaders/shaders_game_of_life.c
$(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \
--preload-file shaders/resources/shaders/glsl100/game_of_life.fs@resources/shaders/glsl100/game_of_life.fs \ --preload-file shaders/resources/shaders/glsl100/game_of_life.fs@resources/shaders/glsl100/game_of_life.fs \
--preload-file shaders/resources/game_of_life/acorn.png@resources/game_of_life/acorn.png \
--preload-file shaders/resources/game_of_life/breeder.png@resources/game_of_life/breeder.png \
--preload-file shaders/resources/game_of_life/glider.png@resources/game_of_life/glider.png \
--preload-file shaders/resources/game_of_life/glider_gun.png@resources/game_of_life/glider_gun.png \
--preload-file shaders/resources/game_of_life/oscillators.png@resources/game_of_life/oscillators.png \
--preload-file shaders/resources/game_of_life/puffer_train.png@resources/game_of_life/puffer_train.png \
--preload-file shaders/resources/game_of_life/r_pentomino.png@resources/game_of_life/r_pentomino.png \ --preload-file shaders/resources/game_of_life/r_pentomino.png@resources/game_of_life/r_pentomino.png \
--preload-file shaders/resources/game_of_life/.png@resources/game_of_life/.png --preload-file shaders/resources/game_of_life/spaceships.png@resources/game_of_life/spaceships.png \
--preload-file shaders/resources/game_of_life/still_lifes.png@resources/game_of_life/still_lifes.png
shaders/shaders_hot_reloading: shaders/shaders_hot_reloading.c shaders/shaders_hot_reloading: shaders/shaders_hot_reloading.c
$(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \

View File

@ -42,7 +42,6 @@ typedef enum {
// Struct to store example preset patterns // Struct to store example preset patterns
typedef struct { typedef struct {
char *name; char *name;
char *fileName;
Vector2 position; Vector2 position;
} PresetPattern; } PresetPattern;
@ -77,10 +76,10 @@ int main(void)
const Rectangle textureOnScreen = { 0, 0, (float)windowWidth, (float)windowHeight }; const Rectangle textureOnScreen = { 0, 0, (float)windowWidth, (float)windowHeight };
const PresetPattern presetPatterns[] = { const PresetPattern presetPatterns[] = {
{ "Glider", "glider", { 0.5f, 0.5f } }, { "R-pentomino", "r_pentomino", { 0.5f, 0.5f } }, { "Acorn", "acorn", { 0.5f,0.5f } }, { "Glider", { 0.5f, 0.5f } }, { "R-pentomino", { 0.5f, 0.5f } }, { "Acorn", { 0.5f,0.5f } },
{ "Spaceships", "spaceships", { 0.1f, 0.5f } }, { "Still lifes", "still_lifes", { 0.5f, 0.5f } }, { "Oscillators", "oscillators", { 0.5f, 0.5f } }, { "Spaceships", { 0.1f, 0.5f } }, { "Still lifes", { 0.5f, 0.5f } }, { "Oscillators", { 0.5f, 0.5f } },
{ "Puffer train", "puffer_train", { 0.1f, 0.5f } }, { "Glider Gun", "glider_gun", { 0.2f, 0.2f } }, { "Breeder", "breeder", { 0.1f, 0.5f } }, { "Puffer train", { 0.1f, 0.5f } }, { "Glider Gun", { 0.2f, 0.2f } }, { "Breeder", { 0.1f, 0.5f } },
{ "Random", "", { 0.5f, 0.5f } } { "Random", { 0.5f, 0.5f } }
}; };
const int numberOfPresets = sizeof(presetPatterns)/sizeof(presetPatterns[0]); const int numberOfPresets = sizeof(presetPatterns)/sizeof(presetPatterns[0]);
@ -214,8 +213,18 @@ int main(void)
Image pattern; Image pattern;
if (preset < numberOfPresets - 1) // Preset with pattern image lo load if (preset < numberOfPresets - 1) // Preset with pattern image lo load
{ {
pattern = LoadImage(TextFormat("resources/game_of_life/%s.png", presetPatterns[preset].fileName)); switch (preset)
{
case 0: pattern = LoadImage("resources/game_of_life/glider.png"); break;
case 1: pattern = LoadImage("resources/game_of_life/r_pentomino.png"); break;
case 2: pattern = LoadImage("resources/game_of_life/acorn.png"); break;
case 3: pattern = LoadImage("resources/game_of_life/spaceships.png"); break;
case 4: pattern = LoadImage("resources/game_of_life/still_lifes.png"); break;
case 5: pattern = LoadImage("resources/game_of_life/oscillators.png"); break;
case 6: pattern = LoadImage("resources/game_of_life/puffer_train.png"); break;
case 7: pattern = LoadImage("resources/game_of_life/glider_gun.png"); break;
case 8: pattern = LoadImage("resources/game_of_life/breeder.png"); break;
}
BeginTextureMode(*currentWorld); BeginTextureMode(*currentWorld);
ClearBackground(RAYWHITE); ClearBackground(RAYWHITE);
EndTextureMode(); EndTextureMode();

View File

@ -1140,7 +1140,7 @@ bool ExportWaveAsCode(Wave wave, const char *fileName)
// Get file name from path and convert variable name to uppercase // Get file name from path and convert variable name to uppercase
char varFileName[256] = { 0 }; char varFileName[256] = { 0 };
strcpy(varFileName, GetFileNameWithoutExt(fileName)); strncpy(varFileName, GetFileNameWithoutExt(fileName), 256 - 1);
for (int i = 0; varFileName[i] != '\0'; i++) if (varFileName[i] >= 'a' && varFileName[i] <= 'z') { varFileName[i] = varFileName[i] - 32; } for (int i = 0; varFileName[i] != '\0'; i++) if (varFileName[i] >= 'a' && varFileName[i] <= 'z') { varFileName[i] = varFileName[i] - 32; }
// Add wave information // Add wave information
@ -2739,11 +2739,13 @@ static const char *GetFileExtension(const char *fileName)
return dot; return dot;
} }
// String pointer reverse break: returns right-most occurrence of charset in s // String pointer reverse break: returns right-most occurrence of charset in text
static const char *strprbrk(const char *s, const char *charset) static const char *strprbrk(const char *text, const char *charset)
{ {
const char *latestMatch = NULL; const char *latestMatch = NULL;
for (; s = strpbrk(s, charset), s != NULL; latestMatch = s++) { }
for (; (text != NULL) && (text = strpbrk(text, charset)); latestMatch = text++) { }
return latestMatch; return latestMatch;
} }
@ -2766,7 +2768,7 @@ static const char *GetFileNameWithoutExt(const char *filePath)
static char fileName[MAX_FILENAMEWITHOUTEXT_LENGTH] = { 0 }; static char fileName[MAX_FILENAMEWITHOUTEXT_LENGTH] = { 0 };
memset(fileName, 0, MAX_FILENAMEWITHOUTEXT_LENGTH); memset(fileName, 0, MAX_FILENAMEWITHOUTEXT_LENGTH);
if (filePath != NULL) strcpy(fileName, GetFileName(filePath)); // Get filename with extension if (filePath != NULL) strncpy(fileName, GetFileName(filePath), MAX_FILENAMEWITHOUTEXT_LENGTH - 1); // Get filename with extension
int size = (int)strlen(fileName); // Get size in bytes int size = (int)strlen(fileName); // Get size in bytes
@ -2864,7 +2866,8 @@ static bool SaveFileText(const char *fileName, char *text)
if (file != NULL) if (file != NULL)
{ {
int count = fprintf(file, "%s", text); int count = 0;
if (text != NULL) count = fprintf(file, "%s", text);
if (count == 0) TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to write text file", fileName); if (count == 0) TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to write text file", fileName);
else TRACELOG(LOG_INFO, "FILEIO: [%s] Text file saved successfully", fileName); else TRACELOG(LOG_INFO, "FILEIO: [%s] Text file saved successfully", fileName);

View File

@ -334,10 +334,10 @@ typedef Camera3D Camera; // Camera type fallback, defaults to Camera3D
// Camera2D, defines position/orientation in 2d space // Camera2D, defines position/orientation in 2d space
typedef struct Camera2D { typedef struct Camera2D {
Vector2 offset; // Camera offset (displacement from target) Vector2 offset; // Camera offset (screen space offset from window origin)
Vector2 target; // Camera target (rotation and zoom origin) Vector2 target; // Camera target (world space target point that is mapped to screen space offset)
float rotation; // Camera rotation in degrees float rotation; // Camera rotation in degrees (pivots around target)
float zoom; // Camera zoom (scaling), should be 1.0f by default float zoom; // Camera zoom (scaling around target), must not be set to 0, set to 1.0f for no scale
} Camera2D; } Camera2D;
// Mesh, vertex data and vao/vbo // Mesh, vertex data and vao/vbo
@ -1145,7 +1145,7 @@ RLAPI const char *GetPrevDirectoryPath(const char *dirPath); // Get previ
RLAPI const char *GetWorkingDirectory(void); // Get current working directory (uses static string) RLAPI const char *GetWorkingDirectory(void); // Get current working directory (uses static string)
RLAPI const char *GetApplicationDirectory(void); // Get the directory of the running application (uses static string) RLAPI const char *GetApplicationDirectory(void); // Get the directory of the running application (uses static string)
RLAPI int MakeDirectory(const char *dirPath); // Create directories (including full path requested), returns 0 on success RLAPI int MakeDirectory(const char *dirPath); // Create directories (including full path requested), returns 0 on success
RLAPI bool ChangeDirectory(const char *dir); // Change working directory, return true on success RLAPI bool ChangeDirectory(const char *dirPath); // Change working directory, return true on success
RLAPI bool IsPathFile(const char *path); // Check if a given path is a file or a directory RLAPI bool IsPathFile(const char *path); // Check if a given path is a file or a directory
RLAPI bool IsFileNameValid(const char *fileName); // Check if fileName is valid for the platform/OS RLAPI bool IsFileNameValid(const char *fileName); // Check if fileName is valid for the platform/OS
RLAPI FilePathList LoadDirectoryFiles(const char *dirPath); // Load directory filepaths RLAPI FilePathList LoadDirectoryFiles(const char *dirPath); // Load directory filepaths

View File

@ -113,7 +113,7 @@
#include <stdlib.h> // Required for: srand(), rand(), atexit() #include <stdlib.h> // Required for: srand(), rand(), atexit()
#include <stdio.h> // Required for: sprintf() [Used in OpenURL()] #include <stdio.h> // Required for: sprintf() [Used in OpenURL()]
#include <string.h> // Required for: strlen(), strcpy(), strcmp(), strrchr(), memset() #include <string.h> // Required for: strlen(), strncpy(), strcmp(), strrchr(), memset()
#include <time.h> // Required for: time() [Used in InitTimer()] #include <time.h> // Required for: time() [Used in InitTimer()]
#include <math.h> // Required for: tan() [Used in BeginMode3D()], atan2f() [Used in LoadVrStereoConfig()] #include <math.h> // Required for: tan() [Used in BeginMode3D()], atan2f() [Used in LoadVrStereoConfig()]
@ -1837,8 +1837,8 @@ void TakeScreenshot(const char *fileName)
unsigned char *imgData = rlReadScreenPixels((int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y)); unsigned char *imgData = rlReadScreenPixels((int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y));
Image image = { imgData, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y), 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; Image image = { imgData, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y), 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 };
char path[512] = { 0 }; char path[MAX_FILEPATH_LENGTH] = { 0 };
strcpy(path, TextFormat("%s/%s", CORE.Storage.basePath, fileName)); strncpy(path, TextFormat("%s/%s", CORE.Storage.basePath, fileName), MAX_FILEPATH_LENGTH - 1);
ExportImage(image, path); // WARNING: Module required: rtextures ExportImage(image, path); // WARNING: Module required: rtextures
RL_FREE(imgData); RL_FREE(imgData);
@ -2022,7 +2022,7 @@ bool IsFileExtension(const char *fileName, const char *ext)
int extLen = (int)strlen(ext); int extLen = (int)strlen(ext);
char *extList = (char *)RL_CALLOC(extLen + 1, 1); char *extList = (char *)RL_CALLOC(extLen + 1, 1);
char *extListPtrs[MAX_FILE_EXTENSIONS] = { 0 }; char *extListPtrs[MAX_FILE_EXTENSIONS] = { 0 };
strcpy(extList, ext); strncpy(extList, ext, extLen);
extListPtrs[0] = extList; extListPtrs[0] = extList;
for (int i = 0; i < extLen; i++) for (int i = 0; i < extLen; i++)
@ -2130,11 +2130,11 @@ const char *GetFileExtension(const char *fileName)
} }
// String pointer reverse break: returns right-most occurrence of charset in s // String pointer reverse break: returns right-most occurrence of charset in s
static const char *strprbrk(const char *s, const char *charset) static const char *strprbrk(const char *text, const char *charset)
{ {
const char *latestMatch = NULL; const char *latestMatch = NULL;
for (; s = strpbrk(s, charset), s != NULL; latestMatch = s++) { } for (; (text != NULL) && (text = strpbrk(text, charset)); latestMatch = text++) { }
return latestMatch; return latestMatch;
} }
@ -2161,7 +2161,7 @@ const char *GetFileNameWithoutExt(const char *filePath)
if (filePath != NULL) if (filePath != NULL)
{ {
strcpy(fileName, GetFileName(filePath)); // Get filename.ext without path strncpy(fileName, GetFileName(filePath), MAX_FILENAME_LENGTH - 1); // Get filename.ext without path
int size = (int)strlen(fileName); // Get size in bytes int size = (int)strlen(fileName); // Get size in bytes
for (int i = size; i > 0; i--) // Reverse search '.' for (int i = size; i > 0; i--) // Reverse search '.'
@ -2233,7 +2233,7 @@ const char *GetPrevDirectoryPath(const char *dirPath)
memset(prevDirPath, 0, MAX_FILEPATH_LENGTH); memset(prevDirPath, 0, MAX_FILEPATH_LENGTH);
int pathLen = (int)strlen(dirPath); int pathLen = (int)strlen(dirPath);
if (pathLen <= 3) strcpy(prevDirPath, dirPath); if (pathLen <= 3) strncpy(prevDirPath, dirPath, MAX_FILEPATH_LENGTH - 1);
for (int i = (pathLen - 1); (i >= 0) && (pathLen > 3); i--) for (int i = (pathLen - 1); (i >= 0) && (pathLen > 3); i--)
{ {
@ -2472,12 +2472,12 @@ int MakeDirectory(const char *dirPath)
} }
// Change working directory, returns true on success // Change working directory, returns true on success
bool ChangeDirectory(const char *dir) bool ChangeDirectory(const char *dirPath)
{ {
bool result = CHDIR(dir); bool result = CHDIR(dirPath);
if (result != 0) TRACELOG(LOG_WARNING, "SYSTEM: Failed to change to directory: %s", dir); if (result != 0) TRACELOG(LOG_WARNING, "SYSTEM: Failed to change to directory: %s", dirPath);
else TRACELOG(LOG_INFO, "SYSTEM: Working Directory: %s", dir); else TRACELOG(LOG_INFO, "SYSTEM: Working Directory: %s", dirPath);
return (result == 0); return (result == 0);
} }
@ -2709,6 +2709,9 @@ unsigned char *DecodeDataBase64(const char *text, int *outputSize)
['8'] = 60, ['9'] = 61, ['+'] = 62, ['/'] = 63 ['8'] = 60, ['9'] = 61, ['+'] = 62, ['/'] = 63
}; };
*outputSize = 0;
if (text == NULL) return NULL;
// Compute expected size and padding // Compute expected size and padding
int dataSize = (int)strlen(text); // WARNING: Expecting NULL terminated strings! int dataSize = (int)strlen(text); // WARNING: Expecting NULL terminated strings!
int ending = dataSize - 1; int ending = dataSize - 1;
@ -3952,7 +3955,7 @@ static void ScanDirectoryFiles(const char *basePath, FilePathList *files, const
{ {
if (IsFileExtension(path, filter)) if (IsFileExtension(path, filter))
{ {
strcpy(files->paths[files->count], path); strncpy(files->paths[files->count], path, MAX_FILEPATH_LENGTH - 1);
files->count++; files->count++;
} }
} }
@ -3960,14 +3963,14 @@ static void ScanDirectoryFiles(const char *basePath, FilePathList *files, const
{ {
if (strstr(filter, DIRECTORY_FILTER_TAG) != NULL) if (strstr(filter, DIRECTORY_FILTER_TAG) != NULL)
{ {
strcpy(files->paths[files->count], path); strncpy(files->paths[files->count], path, MAX_FILEPATH_LENGTH - 1);
files->count++; files->count++;
} }
} }
} }
else else
{ {
strcpy(files->paths[files->count], path); strncpy(files->paths[files->count], path, MAX_FILEPATH_LENGTH - 1);
files->count++; files->count++;
} }
} }
@ -4011,13 +4014,13 @@ static void ScanDirectoryFilesRecursively(const char *basePath, FilePathList *fi
{ {
if (IsFileExtension(path, filter)) if (IsFileExtension(path, filter))
{ {
strcpy(files->paths[files->count], path); strncpy(files->paths[files->count], path, MAX_FILEPATH_LENGTH - 1);
files->count++; files->count++;
} }
} }
else else
{ {
strcpy(files->paths[files->count], path); strncpy(files->paths[files->count], path, MAX_FILEPATH_LENGTH - 1);
files->count++; files->count++;
} }
@ -4031,7 +4034,7 @@ static void ScanDirectoryFilesRecursively(const char *basePath, FilePathList *fi
{ {
if ((filter != NULL) && (strstr(filter, DIRECTORY_FILTER_TAG) != NULL)) if ((filter != NULL) && (strstr(filter, DIRECTORY_FILTER_TAG) != NULL))
{ {
strcpy(files->paths[files->count], path); strncpy(files->paths[files->count], path, MAX_FILEPATH_LENGTH - 1);
files->count++; files->count++;
} }
@ -4335,21 +4338,24 @@ const char *TextFormat(const char *text, ...)
char *currentBuffer = buffers[index]; char *currentBuffer = buffers[index];
memset(currentBuffer, 0, MAX_TEXT_BUFFER_LENGTH); // Clear buffer before using memset(currentBuffer, 0, MAX_TEXT_BUFFER_LENGTH); // Clear buffer before using
va_list args; if (text != NULL)
va_start(args, text);
int requiredByteCount = vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args);
va_end(args);
// If requiredByteCount is larger than the MAX_TEXT_BUFFER_LENGTH, then overflow occurred
if (requiredByteCount >= MAX_TEXT_BUFFER_LENGTH)
{ {
// Inserting "..." at the end of the string to mark as truncated va_list args;
char *truncBuffer = buffers[index] + MAX_TEXT_BUFFER_LENGTH - 4; // Adding 4 bytes = "...\0" va_start(args, text);
snprintf(truncBuffer, 4, "..."); int requiredByteCount = vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args);
} va_end(args);
index += 1; // Move to next buffer for next function call // If requiredByteCount is larger than the MAX_TEXT_BUFFER_LENGTH, then overflow occurred
if (index >= MAX_TEXTFORMAT_BUFFERS) index = 0; if (requiredByteCount >= MAX_TEXT_BUFFER_LENGTH)
{
// Inserting "..." at the end of the string to mark as truncated
char *truncBuffer = buffers[index] + MAX_TEXT_BUFFER_LENGTH - 4; // Adding 4 bytes = "...\0"
snprintf(truncBuffer, 4, "...");
}
index += 1; // Move to next buffer for next function call
if (index >= MAX_TEXTFORMAT_BUFFERS) index = 0;
}
return currentBuffer; return currentBuffer;
} }

View File

@ -2492,12 +2492,12 @@ void rlLoadExtensions(void *loader)
const char *extensions = (const char *)glGetString(GL_EXTENSIONS); // One big const string const char *extensions = (const char *)glGetString(GL_EXTENSIONS); // One big const string
// NOTE: We have to duplicate string because glGetString() returns a const string // NOTE: We have to duplicate string because glGetString() returns a const string
int size = strlen(extensions) + 1; // Get extensions string size in bytes int extSize = (int)strlen(extensions); // Get extensions string size in bytes
char *extensionsDup = (char *)RL_CALLOC(size, sizeof(char)); char *extensionsDup = (char *)RL_CALLOC(extSize + 1, sizeof(char)); // Allocate space for copy with additional EOL byte
strcpy(extensionsDup, extensions); strncpy(extensionsDup, extensions, extSize);
extList[numExt] = extensionsDup; extList[numExt] = extensionsDup;
for (int i = 0; i < size; i++) for (int i = 0; i < extSize; i++)
{ {
if (extensionsDup[i] == ' ') if (extensionsDup[i] == ' ')
{ {

View File

@ -2060,7 +2060,7 @@ bool ExportMeshAsCode(Mesh mesh, const char *fileName)
// Get file name from path and convert variable name to uppercase // Get file name from path and convert variable name to uppercase
char varFileName[256] = { 0 }; char varFileName[256] = { 0 };
strcpy(varFileName, GetFileNameWithoutExt(fileName)); strncpy(varFileName, GetFileNameWithoutExt(fileName), 256 - 1); // NOTE: Using function provided by [rcore] module
for (int i = 0; varFileName[i] != '\0'; i++) if ((varFileName[i] >= 'a') && (varFileName[i] <= 'z')) { varFileName[i] = varFileName[i] - 32; } for (int i = 0; varFileName[i] != '\0'; i++) if ((varFileName[i] >= 'a') && (varFileName[i] <= 'z')) { varFileName[i] = varFileName[i] - 32; }
// Add image information // Add image information
@ -4306,8 +4306,8 @@ static Model LoadOBJ(const char *fileName)
return model; return model;
} }
char currentDir[1024] = { 0 }; char currentDir[MAX_FILEPATH_LENGTH] = { 0 };
strcpy(currentDir, GetWorkingDirectory()); // Save current working directory strncpy(currentDir, GetWorkingDirectory(), MAX_FILEPATH_LENGTH - 1); // Save current working directory
const char *workingDir = GetDirectoryPath(fileName); // Switch to OBJ directory for material path correctness const char *workingDir = GetDirectoryPath(fileName); // Switch to OBJ directory for material path correctness
if (CHDIR(workingDir) != 0) TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to change working directory", workingDir); if (CHDIR(workingDir) != 0) TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to change working directory", workingDir);
@ -5025,10 +5025,8 @@ static ModelAnimation *LoadModelAnimationsIQM(const char *fileName, int *animCou
for (unsigned int j = 0; j < iqmHeader->num_poses; j++) for (unsigned int j = 0; j < iqmHeader->num_poses; j++)
{ {
// If animations and skeleton are in the same file, copy bone names to anim // If animations and skeleton are in the same file, copy bone names to anim
if (iqmHeader->num_joints > 0) if (iqmHeader->num_joints > 0) memcpy(animations[a].bones[j].name, fileDataPtr + iqmHeader->ofs_text + joints[j].name, BONE_NAME_LENGTH*sizeof(char));
memcpy(animations[a].bones[j].name, fileDataPtr + iqmHeader->ofs_text + joints[j].name, BONE_NAME_LENGTH*sizeof(char)); else memcpy(animations[a].bones[j].name, "ANIMJOINTNAME", 13); // Default bone name otherwise
else
strcpy(animations[a].bones[j].name, "ANIMJOINTNAME"); // Default bone name otherwise
animations[a].bones[j].parent = poses[j].parent; animations[a].bones[j].parent = poses[j].parent;
} }
@ -6970,7 +6968,7 @@ static Model LoadM3D(const char *fileName)
// Add a special "no bone" bone // Add a special "no bone" bone
model.bones[i].parent = -1; model.bones[i].parent = -1;
strcpy(model.bones[i].name, "NO BONE"); memcpy(model.bones[i].name, "NO BONE", 7);
model.bindPose[i].translation.x = 0.0f; model.bindPose[i].translation.x = 0.0f;
model.bindPose[i].translation.y = 0.0f; model.bindPose[i].translation.y = 0.0f;
model.bindPose[i].translation.z = 0.0f; model.bindPose[i].translation.z = 0.0f;
@ -7062,7 +7060,7 @@ static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, int *animCou
// A special, never transformed "no bone" bone, used for boneless vertices // A special, never transformed "no bone" bone, used for boneless vertices
animations[a].bones[i].parent = -1; animations[a].bones[i].parent = -1;
strcpy(animations[a].bones[i].name, "NO BONE"); memcpy(animations[a].bones[i].name, "NO BONE", 7);
// M3D stores frames at arbitrary intervals with sparse skeletons. We need full skeletons at // M3D stores frames at arbitrary intervals with sparse skeletons. We need full skeletons at
// regular intervals, so let the M3D SDK do the heavy lifting and calculate interpolated bones // regular intervals, so let the M3D SDK do the heavy lifting and calculate interpolated bones

View File

@ -67,7 +67,7 @@
#include <stdlib.h> // Required for: malloc(), free() #include <stdlib.h> // Required for: malloc(), free()
#include <stdio.h> // Required for: vsprintf() #include <stdio.h> // Required for: vsprintf()
#include <string.h> // Required for: strcmp(), strstr(), strcpy(), strncpy() [Used in TextReplace()], sscanf() [Used in LoadBMFont()] #include <string.h> // Required for: strcmp(), strstr(), strncpy() [Used in TextReplace()], sscanf() [Used in LoadBMFont()]
#include <stdarg.h> // Required for: va_list, va_start(), vsprintf(), va_end() [Used in TextFormat()] #include <stdarg.h> // Required for: va_list, va_start(), vsprintf(), va_end() [Used in TextFormat()]
#include <ctype.h> // Required for: toupper(), tolower() [Used in TextToUpper(), TextToLower()] #include <ctype.h> // Required for: toupper(), tolower() [Used in TextToUpper(), TextToLower()]
@ -164,9 +164,8 @@ extern void LoadFontDefault(void)
{ {
#define BIT_CHECK(a,b) ((a) & (1u << (b))) #define BIT_CHECK(a,b) ((a) & (1u << (b)))
// check to see if we have allready allocated the font for an image, and if we don't need to upload, then just return // Check to see if we have allready allocated the font for an image, and if we don't need to upload, then just return
if (defaultFont.glyphs != NULL && !isGpuReady) if ((defaultFont.glyphs != NULL) && !isGpuReady) return;
return;
// NOTE: Using UTF-8 encoding table for Unicode U+0000..U+00FF Basic Latin + Latin-1 Supplement // NOTE: Using UTF-8 encoding table for Unicode U+0000..U+00FF Basic Latin + Latin-1 Supplement
// Ref: http://www.utf8-chartable.de/unicode-utf8-table.pl // Ref: http://www.utf8-chartable.de/unicode-utf8-table.pl
@ -1453,29 +1452,31 @@ Rectangle GetGlyphAtlasRec(Font font, int codepoint)
char **LoadTextLines(const char *text, int *count) char **LoadTextLines(const char *text, int *count)
{ {
char **lines = NULL; char **lines = NULL;
int lineCount = 0;
if (text == NULL) { *count = 0; return lines; } if (text != NULL)
int lineCount = 1;
int textSize = (int)strlen(text);
// First text scan pass to get required line count
for (int i = 0; i < textSize; i++)
{ {
if (text[i] == '\n') lineCount++; int textSize = TextLength(text);
} lineCount = 1;
lines = (char **)RL_CALLOC(lineCount, sizeof(char *)); // First text scan pass to get required line count
for (int i = 0, l = 0, lineLen = 0; i <= textSize; i++) for (int i = 0; i < textSize; i++)
{
if ((text[i] == '\n') || (text[i] == '\0'))
{ {
lines[l] = (char *)RL_CALLOC(lineLen + 1, 1); if (text[i] == '\n') lineCount++;
strncpy(lines[l], &text[i - lineLen], lineLen); }
lineLen = 0;
l++; lines = (char **)RL_CALLOC(lineCount, sizeof(char *));
for (int i = 0, l = 0, lineLen = 0; i <= textSize; i++)
{
if ((text[i] == '\n') || (text[i] == '\0'))
{
lines[l] = (char *)RL_CALLOC(lineLen + 1, 1);
strncpy(lines[l], &text[i - lineLen], lineLen);
lineLen = 0;
l++;
}
else lineLen++;
} }
else lineLen++;
} }
*count = lineCount; *count = lineCount;
@ -1517,23 +1518,26 @@ const char *TextFormat(const char *text, ...)
static int index = 0; static int index = 0;
char *currentBuffer = buffers[index]; char *currentBuffer = buffers[index];
memset(currentBuffer, 0, MAX_TEXT_BUFFER_LENGTH); // Clear buffer before using memset(currentBuffer, 0, MAX_TEXT_BUFFER_LENGTH); // Clear buffer before using
va_list args; if (text != NULL)
va_start(args, text);
int requiredByteCount = vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args);
va_end(args);
// If requiredByteCount is larger than the MAX_TEXT_BUFFER_LENGTH, then overflow occurred
if (requiredByteCount >= MAX_TEXT_BUFFER_LENGTH)
{ {
// Inserting "..." at the end of the string to mark as truncated va_list args;
char *truncBuffer = buffers[index] + MAX_TEXT_BUFFER_LENGTH - 4; // Adding 4 bytes = "...\0" va_start(args, text);
snprintf(truncBuffer, 4, "..."); int requiredByteCount = vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args);
} va_end(args);
index += 1; // Move to next buffer for next function call // If requiredByteCount is larger than the MAX_TEXT_BUFFER_LENGTH, then overflow occurred
if (index >= MAX_TEXTFORMAT_BUFFERS) index = 0; if (requiredByteCount >= MAX_TEXT_BUFFER_LENGTH)
{
// Inserting "..." at the end of the string to mark as truncated
char *truncBuffer = buffers[index] + MAX_TEXT_BUFFER_LENGTH - 4; // Adding 4 bytes = "...\0"
snprintf(truncBuffer, 4, "...");
}
index += 1; // Move to next buffer for next function call
if (index >= MAX_TEXTFORMAT_BUFFERS) index = 0;
}
return currentBuffer; return currentBuffer;
} }
@ -1545,13 +1549,16 @@ int TextToInteger(const char *text)
int value = 0; int value = 0;
int sign = 1; int sign = 1;
if ((text[0] == '+') || (text[0] == '-')) if (text != NULL)
{ {
if (text[0] == '-') sign = -1; if ((text[0] == '+') || (text[0] == '-'))
text++; {
} if (text[0] == '-') sign = -1;
text++;
}
for (int i = 0; ((text[i] >= '0') && (text[i] <= '9')); i++) value = value*10 + (int)(text[i] - '0'); for (int i = 0; ((text[i] >= '0') && (text[i] <= '9')); i++) value = value*10 + (int)(text[i] - '0');
}
return value*sign; return value*sign;
} }
@ -1564,22 +1571,25 @@ float TextToFloat(const char *text)
float value = 0.0f; float value = 0.0f;
float sign = 1.0f; float sign = 1.0f;
if ((text[0] == '+') || (text[0] == '-')) if (text != NULL)
{ {
if (text[0] == '-') sign = -1.0f; if ((text[0] == '+') || (text[0] == '-'))
text++;
}
int i = 0;
for (; ((text[i] >= '0') && (text[i] <= '9')); i++) value = value*10.0f + (float)(text[i] - '0');
if (text[i++] == '.')
{
float divisor = 10.0f;
for (; ((text[i] >= '0') && (text[i] <= '9')); i++)
{ {
value += ((float)(text[i] - '0'))/divisor; if (text[0] == '-') sign = -1.0f;
divisor = divisor*10.0f; text++;
}
int i = 0;
for (; ((text[i] >= '0') && (text[i] <= '9')); i++) value = value*10.0f + (float)(text[i] - '0');
if (text[i++] == '.')
{
float divisor = 10.0f;
for (; ((text[i] >= '0') && (text[i] <= '9')); i++)
{
value += ((float)(text[i] - '0'))/divisor;
divisor = divisor*10.0f;
}
} }
} }
@ -1631,26 +1641,23 @@ const char *TextSubtext(const char *text, int position, int length)
static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 }; static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH); memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH);
int textLength = TextLength(text); if (text != NULL)
if (position >= textLength)
{ {
return buffer; //First char is already '\0' by memset int textLength = TextLength(text);
if (position >= textLength) return buffer; // First char is already '\0' by memset
int maxLength = textLength - position;
if (length > maxLength) length = maxLength;
if (length >= MAX_TEXT_BUFFER_LENGTH) length = MAX_TEXT_BUFFER_LENGTH - 1;
// NOTE: Alternative: memcpy(buffer, text + position, length)
for (int c = 0; c < length; c++) buffer[c] = text[position + c];
buffer[length] = '\0';
} }
int maxLength = textLength - position;
if (length > maxLength) length = maxLength;
if (length >= MAX_TEXT_BUFFER_LENGTH) length = MAX_TEXT_BUFFER_LENGTH - 1;
// NOTE: Alternative: memcpy(buffer, text + position, length)
for (int c = 0 ; c < length ; c++)
{
buffer[c] = text[position + c];
}
buffer[length] = '\0';
return buffer; return buffer;
} }
@ -1684,7 +1691,7 @@ char *GetTextBetween(const char *text, const char *begin, const char *end)
if (beginIndex > -1) if (beginIndex > -1)
{ {
int beginLen = (int)strlen(begin); int beginLen = TextLength(begin);
int endIndex = TextFindIndex(text + beginIndex + beginLen, end); int endIndex = TextFindIndex(text + beginIndex + beginLen, end);
if (endIndex > -1) if (endIndex > -1)
@ -1700,84 +1707,86 @@ char *GetTextBetween(const char *text, const char *begin, const char *end)
} }
// Replace text string // Replace text string
// REQUIRES: strstr(), strncpy(), strcpy() // REQUIRES: strstr(), strncpy()
// TODO: If (replacement == "") remove "search" text // TODO: If (replacement == "") remove "search" text
// WARNING: Allocated memory must be manually freed // WARNING: Allocated memory must be manually freed
char *TextReplace(const char *text, const char *search, const char *replacement) char *TextReplace(const char *text, const char *search, const char *replacement)
{ {
char *result = NULL; char *result = NULL;
if (!text || !search) return NULL; // Sanity check if ((text != NULL) && (search != NULL))
char *insertPoint = NULL; // Next insert point
char *temp = NULL; // Temp pointer
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
searchLen = TextLength(search);
if (searchLen == 0) return NULL; // Empty search causes infinite loop during count
replaceLen = TextLength(replacement);
// Count the number of replacements needed
insertPoint = (char *)text;
for (count = 0; (temp = strstr(insertPoint, search)); count++) insertPoint = temp + searchLen;
// Allocate returning string and point temp to it
temp = result = (char *)RL_MALLOC(TextLength(text) + (replaceLen - searchLen)*count + 1);
if (!result) return NULL; // Memory could not be allocated
// 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--)
{ {
insertPoint = (char *)strstr(text, search); char *insertPoint = NULL; // Next insert point
lastReplacePos = (int)(insertPoint - text); char *temp = NULL; // Temp pointer
temp = strncpy(temp, text, lastReplacePos) + lastReplacePos; int searchLen = 0; // Search string length of (the string to remove)
temp = strcpy(temp, replacement) + replaceLen; int replaceLen = 0; // Replacement length (the string to replace by)
text += lastReplacePos + searchLen; // Move to next "end of replace" int lastReplacePos = 0; // Distance between next search and end of last replace
} int count = 0; // Number of replacements
// Copy remaind text part after replacement to result (pointed by moving temp) searchLen = TextLength(search);
strcpy(temp, text); if (searchLen == 0) return NULL; // Empty search causes infinite loop during count
replaceLen = TextLength(replacement);
// Count the number of replacements needed
insertPoint = (char *)text;
for (count = 0; (temp = strstr(insertPoint, search)); count++) insertPoint = temp + searchLen;
// Allocate returning string and point temp to it
temp = result = (char *)RL_MALLOC(TextLength(text) + (replaceLen - searchLen)*count + 1);
if (!result) return NULL; // Memory could not be allocated
// 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--)
{
insertPoint = (char *)strstr(text, search);
lastReplacePos = (int)(insertPoint - text);
temp = strncpy(temp, text, lastReplacePos) + lastReplacePos;
temp = strcpy(temp, replacement) + replaceLen;
text += lastReplacePos + searchLen; // Move to next "end of replace"
}
// Copy remaind text part after replacement to result (pointed by moving temp)
strcpy(temp, text);
}
return result; return result;
} }
// Replace text between two specific strings // Replace text between two specific strings
// REQUIRES: strlen(), strncpy() // REQUIRES: strncpy()
// NOTE: If (replacement == NULL) remove "begin"[ ]"end" text // NOTE: If (replacement == NULL) remove "begin"[ ]"end" text
// WARNING: Returned string must be freed by user // WARNING: Returned string must be freed by user
char *TextReplaceBetween(const char *text, const char *begin, const char *end, const char *replacement) char *TextReplaceBetween(const char *text, const char *begin, const char *end, const char *replacement)
{ {
char *result = NULL; char *result = NULL;
if (!text || !begin || !end) return NULL; // Sanity check if ((text != NULL) && (begin != NULL) && (end != NULL))
int beginIndex = TextFindIndex(text, begin);
if (beginIndex > -1)
{ {
int beginLen = (int)strlen(begin); int beginIndex = TextFindIndex(text, begin);
int endIndex = TextFindIndex(text + beginIndex + beginLen, end);
if (endIndex > -1) if (beginIndex > -1)
{ {
endIndex += (beginIndex + beginLen); int beginLen = TextLength(begin);
int endIndex = TextFindIndex(text + beginIndex + beginLen, end);
int textLen = (int)strlen(text); if (endIndex > -1)
int replaceLen = (replacement == NULL)? 0 : (int)strlen(replacement); {
int toreplaceLen = endIndex - beginIndex - beginLen; endIndex += (beginIndex + beginLen);
result = (char *)RL_CALLOC(textLen + replaceLen - toreplaceLen + 1, sizeof(char));
strncpy(result, text, beginIndex + beginLen); // Copy first text part int textLen = TextLength(text);
if (replacement != NULL) strncpy(result + beginIndex + beginLen, replacement, replaceLen); // Copy replacement (if provided) int replaceLen = (replacement == NULL)? 0 : TextLength(replacement);
strncpy(result + beginIndex + beginLen + replaceLen, text + endIndex, textLen - endIndex); // Copy end text part int toreplaceLen = endIndex - beginIndex - beginLen;
result = (char *)RL_CALLOC(textLen + replaceLen - toreplaceLen + 1, sizeof(char));
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
}
} }
} }
@ -1788,16 +1797,21 @@ char *TextReplaceBetween(const char *text, const char *begin, const char *end, c
// WARNING: Allocated memory must be manually freed // WARNING: Allocated memory must be manually freed
char *TextInsert(const char *text, const char *insert, int position) char *TextInsert(const char *text, const char *insert, int position)
{ {
int textLen = TextLength(text); char *result = NULL;
int insertLen = TextLength(insert);
char *result = (char *)RL_MALLOC(textLen + insertLen + 1); if ((text != NULL) && (insert != NULL))
{
int textLen = TextLength(text);
int insertLen = TextLength(insert);
for (int i = 0; i < position; i++) result[i] = text[i]; result = (char *)RL_MALLOC(textLen + insertLen + 1);
for (int i = position; i < insertLen + position; i++) result[i] = insert[i];
for (int i = (insertLen + position); i < (textLen + insertLen); i++) result[i] = text[i];
result[textLen + insertLen] = '\0'; // Make sure text string is valid! for (int i = 0; i < position; i++) result[i] = text[i];
for (int i = position; i < insertLen + position; i++) result[i] = insert[i];
for (int i = (insertLen + position); i < (textLen + insertLen); i++) result[i] = text[i];
result[textLen + insertLen] = '\0'; // Add EOL
}
return result; return result;
} }
@ -1879,11 +1893,13 @@ char **TextSplit(const char *text, char delimiter, int *count)
// Append text at specific position and move cursor // Append text at specific position and move cursor
// WARNING: It's up to the user to make sure appended text does not overflow the buffer! // WARNING: It's up to the user to make sure appended text does not overflow the buffer!
// REQUIRES: strcpy()
void TextAppend(char *text, const char *append, int *position) void TextAppend(char *text, const char *append, int *position)
{ {
strcpy(text + *position, append); if ((text != NULL) && (append != NULL))
*position += TextLength(append); {
TextCopy(text + *position, append);
*position += TextLength(append);
}
} }
// Find first text occurrence within a string // Find first text occurrence within a string
@ -1891,11 +1907,13 @@ void TextAppend(char *text, const char *append, int *position)
int TextFindIndex(const char *text, const char *search) int TextFindIndex(const char *text, const char *search)
{ {
int position = -1; int position = -1;
if (text == NULL) return position;
char *ptr = (char *)strstr(text, search); if (text != NULL)
{
char *ptr = (char *)strstr(text, search);
if (ptr != NULL) position = (int)(ptr - text); if (ptr != NULL) position = (int)(ptr - text);
}
return position; return position;
} }
@ -2029,24 +2047,29 @@ char *TextToCamel(const char *text)
// WARNING: Allocated memory must be manually freed // WARNING: Allocated memory must be manually freed
char *LoadUTF8(const int *codepoints, int length) char *LoadUTF8(const int *codepoints, int length)
{ {
// We allocate enough memory to fit all possible codepoints char *text = NULL;
// NOTE: 5 bytes for every codepoint should be enough
char *text = (char *)RL_CALLOC(length*5, 1);
const char *utf8 = NULL;
int size = 0;
for (int i = 0, bytes = 0; i < length; i++) if ((codepoints != NULL) && (length > 0))
{ {
utf8 = CodepointToUTF8(codepoints[i], &bytes); // We allocate enough memory to fit all possible codepoints
memcpy(text + size, utf8, bytes); // NOTE: 5 bytes for every codepoint should be enough
size += bytes; text = (char *)RL_CALLOC(length*5, 1);
} const char *utf8 = NULL;
int size = 0;
// Create second buffer and copy data manually to it for (int i = 0, bytes = 0; i < length; i++)
char *temp = (char *)RL_CALLOC(size + 1, 1); {
memcpy(temp, text, size); utf8 = CodepointToUTF8(codepoints[i], &bytes);
RL_FREE(text); memcpy(text + size, utf8, bytes);
text = temp; size += bytes;
}
// Create second buffer and copy data manually to it
char *temp = (char *)RL_CALLOC(size + 1, 1);
memcpy(temp, text, size);
RL_FREE(text);
text = temp;
}
return text; return text;
} }
@ -2060,28 +2083,31 @@ void UnloadUTF8(char *text)
// Load all codepoints from a UTF-8 text string, codepoints count returned by parameter // Load all codepoints from a UTF-8 text string, codepoints count returned by parameter
int *LoadCodepoints(const char *text, int *count) int *LoadCodepoints(const char *text, int *count)
{ {
int textLength = TextLength(text); int *codepoints = NULL;
int codepointSize = 0;
int codepointCount = 0; int codepointCount = 0;
// Allocate a big enough buffer to store as many codepoints as text bytes if (text != NULL)
int *codepoints = (int *)RL_CALLOC(textLength, sizeof(int));
for (int i = 0; i < textLength; codepointCount++)
{ {
codepoints[codepointCount] = GetCodepointNext(text + i, &codepointSize); int textLength = TextLength(text);
i += codepointSize;
// Allocate a big enough buffer to store as many codepoints as text bytes
int *codepoints = (int *)RL_CALLOC(textLength, sizeof(int));
int codepointSize = 0;
for (int i = 0; i < textLength; codepointCount++)
{
codepoints[codepointCount] = GetCodepointNext(text + i, &codepointSize);
i += codepointSize;
}
// Create second buffer and copy data manually to it
int *temp = (int *)RL_CALLOC(codepointCount, sizeof(int));
for (int i = 0; i < codepointCount; i++) temp[i] = codepoints[i];
RL_FREE(codepoints);
codepoints = temp;
} }
// Create second buffer and copy data manually to it
int *temp = (int *)RL_CALLOC(codepointCount, sizeof(int));
for (int i = 0; i < codepointCount; i++) temp[i] = codepoints[i];
RL_FREE(codepoints);
codepoints = temp;
*count = codepointCount; *count = codepointCount;
return codepoints; return codepoints;
} }
@ -2098,14 +2124,15 @@ int GetCodepointCount(const char *text)
unsigned int length = 0; unsigned int length = 0;
const char *ptr = text; const char *ptr = text;
while (*ptr != '\0') if (ptr != NULL)
{ {
int next = 0; while (*ptr != '\0')
GetCodepointNext(ptr, &next); {
int next = 0;
ptr += next; GetCodepointNext(ptr, &next);
ptr += next;
length++; length++;
}
} }
return length; return length;
@ -2170,11 +2197,14 @@ int GetCodepoint(const char *text, int *codepointSize)
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
*/ */
// NOTE: on decode errors we return as soon as possible
int codepoint = 0x3f; // Codepoint (defaults to '?') int codepoint = 0x3f; // Codepoint (defaults to '?')
int octet = (unsigned char)(text[0]); // The first UTF8 octet
*codepointSize = 1; *codepointSize = 1;
if (text == NULL) return codepoint;
// NOTE: on decode errors we return as soon as possible
int octet = (unsigned char)(text[0]); // The first UTF8 octet
if (octet <= 0x7f) if (octet <= 0x7f)
{ {
@ -2266,6 +2296,7 @@ int GetCodepointNext(const char *text, int *codepointSize)
const char *ptr = text; const char *ptr = text;
int codepoint = 0x3f; // Codepoint (defaults to '?') int codepoint = 0x3f; // Codepoint (defaults to '?')
*codepointSize = 1; *codepointSize = 1;
if (text == NULL) return codepoint;
// Get current codepoint and bytes processed // Get current codepoint and bytes processed
if (0xf0 == (0xf8 & ptr[0])) if (0xf0 == (0xf8 & ptr[0]))
@ -2304,15 +2335,15 @@ int GetCodepointPrevious(const char *text, int *codepointSize)
{ {
const char *ptr = text; const char *ptr = text;
int codepoint = 0x3f; // Codepoint (defaults to '?') int codepoint = 0x3f; // Codepoint (defaults to '?')
int cpSize = 0; *codepointSize = 1;
*codepointSize = 0; if (text == NULL) return codepoint;
// Move to previous codepoint // Move to previous codepoint
do ptr--; do ptr--;
while (((0x80 & ptr[0]) != 0) && ((0xc0 & ptr[0]) == 0x80)); while (((0x80 & ptr[0]) != 0) && ((0xc0 & ptr[0]) == 0x80));
int cpSize = 0;
codepoint = GetCodepointNext(ptr, &cpSize); codepoint = GetCodepointNext(ptr, &cpSize);
if (codepoint != 0) *codepointSize = cpSize; if (codepoint != 0) *codepointSize = cpSize;
return codepoint; return codepoint;

View File

@ -771,7 +771,7 @@ bool ExportImageAsCode(Image image, const char *fileName)
// Get file name from path and convert variable name to uppercase // Get file name from path and convert variable name to uppercase
char varFileName[256] = { 0 }; char varFileName[256] = { 0 };
strcpy(varFileName, GetFileNameWithoutExt(fileName)); strncpy(varFileName, GetFileNameWithoutExt(fileName), 256 - 1); // NOTE: Using function provided by [rcore] module
for (int i = 0; varFileName[i] != '\0'; i++) if ((varFileName[i] >= 'a') && (varFileName[i] <= 'z')) { varFileName[i] = varFileName[i] - 32; } for (int i = 0; varFileName[i] != '\0'; i++) if ((varFileName[i] >= 'a') && (varFileName[i] <= 'z')) { varFileName[i] = varFileName[i] - 32; }
// Add image information // Add image information
@ -1126,16 +1126,18 @@ Image GenImageText(int width, int height, const char *text)
{ {
Image image = { 0 }; Image image = { 0 };
int textLength = (int)strlen(text); int imageSize = width*height;
int imageViewSize = width*height;
image.width = width; image.width = width;
image.height = height; image.height = height;
image.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE; image.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE;
image.data = RL_CALLOC(imageViewSize, 1); image.data = RL_CALLOC(imageSize, 1);
image.mipmaps = 1; image.mipmaps = 1;
memcpy(image.data, text, (textLength > imageViewSize)? imageViewSize : textLength); if (text != NULL)
{
int textLength = (int)strlen(text);
memcpy(image.data, text, (textLength > imageSize)? imageSize : textLength);
}
return image; return image;
} }
@ -1484,8 +1486,9 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co
{ {
Image imText = { 0 }; Image imText = { 0 };
#if defined(SUPPORT_MODULE_RTEXT) #if defined(SUPPORT_MODULE_RTEXT)
int size = (int)strlen(text); // Get size in bytes of text if (text == NULL) return imText;
int size = (int)strlen(text); // Get size in bytes of text
int textOffsetX = 0; // Image drawing position X int textOffsetX = 0; // Image drawing position X
int textOffsetY = 0; // Offset between lines (on linebreak '\n') int textOffsetY = 0; // Offset between lines (on linebreak '\n')

View File

@ -105,7 +105,7 @@ void TraceLog(int logType, const char *text, ...)
{ {
#if defined(SUPPORT_TRACELOG) #if defined(SUPPORT_TRACELOG)
// Message has level below current threshold, don't emit // Message has level below current threshold, don't emit
if (logType < logTypeLevel) return; if ((logType < logTypeLevel) || (text == NULL)) return;
va_list args; va_list args;
va_start(args, text); va_start(args, text);
@ -313,7 +313,7 @@ bool ExportDataAsCode(const unsigned char *data, int dataSize, const char *fileN
// Get file name from path // Get file name from path
char varFileName[256] = { 0 }; char varFileName[256] = { 0 };
strcpy(varFileName, GetFileNameWithoutExt(fileName)); strncpy(varFileName, GetFileNameWithoutExt(fileName), 256 - 1);
for (int i = 0; varFileName[i] != '\0'; i++) for (int i = 0; varFileName[i] != '\0'; i++)
{ {
// Convert variable name to uppercase // Convert variable name to uppercase

View File

@ -1008,6 +1008,7 @@ int main(int argc, char *argv[])
LOG("INFO: Scanning available example (.c) files to be added to collection...\n"); LOG("INFO: Scanning available example (.c) files to be added to collection...\n");
FilePathList clist = LoadDirectoryFilesEx(exBasePath, ".c", true); FilePathList clist = LoadDirectoryFilesEx(exBasePath, ".c", true);
// Load examples collection list file (raylib/examples/examples_list.txt)
char *exList = LoadFileText(exCollectionFilePath); char *exList = LoadFileText(exCollectionFilePath);
char *exListUpdated = (char *)RL_CALLOC(REXM_MAX_BUFFER_SIZE, 1); char *exListUpdated = (char *)RL_CALLOC(REXM_MAX_BUFFER_SIZE, 1);
bool listUpdated = false; bool listUpdated = false;
@ -1024,7 +1025,7 @@ int main(int argc, char *argv[])
for (int i = 1; i < lineCount; i++) for (int i = 1; i < lineCount; i++)
{ {
if ((TextFindIndex(exListUpdated, exListLines[i]) == -1) || (exListLines[i][0] == '#')) if ((TextFindIndex(exListUpdated, exListLines[i]) == -1) || (exListLines[i][0] == '#'))
exListUpdatedOffset += sprintf(exListUpdated + exListUpdatedOffset, "%s\n", exListLines[i]); exListUpdatedOffset += sprintf(exListUpdated + exListUpdatedOffset, "%s\n", exListLines[i]);
else listUpdated = true; else listUpdated = true;
} }
@ -2328,7 +2329,7 @@ static rlExampleInfo *LoadExampleInfo(const char *exFileName)
// Example found in collection // Example found in collection
exInfo = (rlExampleInfo *)RL_CALLOC(1, sizeof(rlExampleInfo)); exInfo = (rlExampleInfo *)RL_CALLOC(1, sizeof(rlExampleInfo));
strcpy(exInfo->name, GetFileNameWithoutExt(exFileName)); strncpy(exInfo->name, GetFileNameWithoutExt(exFileName), 128 - 1);
strncpy(exInfo->category, exInfo->name, TextFindIndex(exInfo->name, "_")); strncpy(exInfo->category, exInfo->name, TextFindIndex(exInfo->name, "_"));
char *exText = LoadFileText(exFileName); char *exText = LoadFileText(exFileName);
@ -2511,7 +2512,7 @@ static char **LoadExampleResourcePaths(const char *filePath, int *resPathCount)
if (!end) break; if (!end) break;
// WARNING: Some paths could be for saving files, not loading, those "resource" files must be omitted // WARNING: Some paths could be for saving files, not loading, those "resource" files must be omitted
// HACK: Just check previous position from pointer for function name including the string and the index "distance" // TODO: HACK: Just check previous position from pointer for function name including the string and the index "distance"
// This is a quick solution, the good one would be getting the data loading function names... // This is a quick solution, the good one would be getting the data loading function names...
int functionIndex01 = TextFindIndex(ptr - 40, "ExportImage"); // Check ExportImage() int functionIndex01 = TextFindIndex(ptr - 40, "ExportImage"); // Check ExportImage()
int functionIndex02 = TextFindIndex(ptr - 10, "TraceLog"); // Check TraceLog() int functionIndex02 = TextFindIndex(ptr - 10, "TraceLog"); // Check TraceLog()
@ -2869,8 +2870,8 @@ static void UpdateWebMetadata(const char *exHtmlPath, const char *exFilePath)
char exTitle[64] = { 0 }; // Example title: fileName without extension, replacing underscores by spaces char exTitle[64] = { 0 }; // Example title: fileName without extension, replacing underscores by spaces
// Get example name: replace underscore by spaces // Get example name: replace underscore by spaces
strcpy(exName, GetFileNameWithoutExt(exHtmlPathCopy)); strncpy(exName, GetFileNameWithoutExt(exHtmlPathCopy), 64 - 1);
strcpy(exTitle, exName); strncpy(exTitle, exName, 64 - 1);
for (int i = 0; (i < 256) && (exTitle[i] != '\0'); i++) { if (exTitle[i] == '_') exTitle[i] = ' '; } for (int i = 0; (i < 256) && (exTitle[i] != '\0'); i++) { if (exTitle[i] == '_') exTitle[i] = ' '; }
// Get example category from exName: copy until first underscore // Get example category from exName: copy until first underscore

View File

@ -769,22 +769,22 @@
{ {
"type": "Vector2", "type": "Vector2",
"name": "offset", "name": "offset",
"description": "Camera offset (displacement from target)" "description": "Camera offset (screen space offset from window origin)"
}, },
{ {
"type": "Vector2", "type": "Vector2",
"name": "target", "name": "target",
"description": "Camera target (rotation and zoom origin)" "description": "Camera target (world space target point that is mapped to screen space offset)"
}, },
{ {
"type": "float", "type": "float",
"name": "rotation", "name": "rotation",
"description": "Camera rotation in degrees" "description": "Camera rotation in degrees (pivots around target)"
}, },
{ {
"type": "float", "type": "float",
"name": "zoom", "name": "zoom",
"description": "Camera zoom (scaling), should be 1.0f by default" "description": "Camera zoom (scaling around target), must not be set to 0, set to 1.0f for no scale"
} }
] ]
}, },
@ -4646,7 +4646,7 @@
"params": [ "params": [
{ {
"type": "const char *", "type": "const char *",
"name": "dir" "name": "dirPath"
} }
] ]
}, },

View File

@ -769,22 +769,22 @@ return {
{ {
type = "Vector2", type = "Vector2",
name = "offset", name = "offset",
description = "Camera offset (displacement from target)" description = "Camera offset (screen space offset from window origin)"
}, },
{ {
type = "Vector2", type = "Vector2",
name = "target", name = "target",
description = "Camera target (rotation and zoom origin)" description = "Camera target (world space target point that is mapped to screen space offset)"
}, },
{ {
type = "float", type = "float",
name = "rotation", name = "rotation",
description = "Camera rotation in degrees" description = "Camera rotation in degrees (pivots around target)"
}, },
{ {
type = "float", type = "float",
name = "zoom", name = "zoom",
description = "Camera zoom (scaling), should be 1.0f by default" description = "Camera zoom (scaling around target), must not be set to 0, set to 1.0f for no scale"
} }
} }
}, },
@ -4167,7 +4167,7 @@ return {
description = "Change working directory, return true on success", description = "Change working directory, return true on success",
returnType = "bool", returnType = "bool",
params = { params = {
{type = "const char *", name = "dir"} {type = "const char *", name = "dirPath"}
} }
}, },
{ {

View File

@ -399,10 +399,10 @@ Struct 13: Camera3D (5 fields)
Struct 14: Camera2D (4 fields) Struct 14: Camera2D (4 fields)
Name: Camera2D Name: Camera2D
Description: Camera2D, defines position/orientation in 2d space Description: Camera2D, defines position/orientation in 2d space
Field[1]: Vector2 offset // Camera offset (displacement from target) Field[1]: Vector2 offset // Camera offset (screen space offset from window origin)
Field[2]: Vector2 target // Camera target (rotation and zoom origin) Field[2]: Vector2 target // Camera target (world space target point that is mapped to screen space offset)
Field[3]: float rotation // Camera rotation in degrees Field[3]: float rotation // Camera rotation in degrees (pivots around target)
Field[4]: float zoom // Camera zoom (scaling), should be 1.0f by default Field[4]: float zoom // Camera zoom (scaling around target), must not be set to 0, set to 1.0f for no scale
Struct 15: Mesh (17 fields) Struct 15: Mesh (17 fields)
Name: Mesh Name: Mesh
Description: Mesh, vertex data and vao/vbo Description: Mesh, vertex data and vao/vbo
@ -1763,7 +1763,7 @@ Function 143: ChangeDirectory() (1 input parameters)
Name: ChangeDirectory Name: ChangeDirectory
Return type: bool Return type: bool
Description: Change working directory, return true on success Description: Change working directory, return true on success
Param[1]: dir (type: const char *) Param[1]: dirPath (type: const char *)
Function 144: IsPathFile() (1 input parameters) Function 144: IsPathFile() (1 input parameters)
Name: IsPathFile Name: IsPathFile
Return type: bool Return type: bool

View File

@ -155,10 +155,10 @@
<Field type="int" name="projection" desc="Camera projection: CAMERA_PERSPECTIVE or CAMERA_ORTHOGRAPHIC" /> <Field type="int" name="projection" desc="Camera projection: CAMERA_PERSPECTIVE or CAMERA_ORTHOGRAPHIC" />
</Struct> </Struct>
<Struct name="Camera2D" fieldCount="4" desc="Camera2D, defines position/orientation in 2d space"> <Struct name="Camera2D" fieldCount="4" desc="Camera2D, defines position/orientation in 2d space">
<Field type="Vector2" name="offset" desc="Camera offset (displacement from target)" /> <Field type="Vector2" name="offset" desc="Camera offset (screen space offset from window origin)" />
<Field type="Vector2" name="target" desc="Camera target (rotation and zoom origin)" /> <Field type="Vector2" name="target" desc="Camera target (world space target point that is mapped to screen space offset)" />
<Field type="float" name="rotation" desc="Camera rotation in degrees" /> <Field type="float" name="rotation" desc="Camera rotation in degrees (pivots around target)" />
<Field type="float" name="zoom" desc="Camera zoom (scaling), should be 1.0f by default" /> <Field type="float" name="zoom" desc="Camera zoom (scaling around target), must not be set to 0, set to 1.0f for no scale" />
</Struct> </Struct>
<Struct name="Mesh" fieldCount="17" desc="Mesh, vertex data and vao/vbo"> <Struct name="Mesh" fieldCount="17" desc="Mesh, vertex data and vao/vbo">
<Field type="int" name="vertexCount" desc="Number of vertices stored in arrays" /> <Field type="int" name="vertexCount" desc="Number of vertices stored in arrays" />
@ -1111,7 +1111,7 @@
<Param type="const char *" name="dirPath" desc="" /> <Param type="const char *" name="dirPath" desc="" />
</Function> </Function>
<Function name="ChangeDirectory" retType="bool" paramCount="1" desc="Change working directory, return true on success"> <Function name="ChangeDirectory" retType="bool" paramCount="1" desc="Change working directory, return true on success">
<Param type="const char *" name="dir" desc="" /> <Param type="const char *" name="dirPath" desc="" />
</Function> </Function>
<Function name="IsPathFile" retType="bool" paramCount="1" desc="Check if a given path is a file or a directory"> <Function name="IsPathFile" retType="bool" paramCount="1" desc="Check if a given path is a file or a directory">
<Param type="const char *" name="path" desc="" /> <Param type="const char *" name="path" desc="" />