mirror of
https://github.com/raysan5/raylib.git
synced 2025-12-15 02:45:32 +00:00
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)
This commit is contained in:
15
src/raudio.c
15
src/raudio.c
@@ -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);
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
70
src/rcore.c
70
src/rcore.c
@@ -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);
|
||||||
}
|
}
|
||||||
@@ -2708,6 +2708,9 @@ unsigned char *DecodeDataBase64(const char *text, int *outputSize)
|
|||||||
['0'] = 52, ['1'] = 53, ['2'] = 54, ['3'] = 55, ['4'] = 56, ['5'] = 57, ['6'] = 58, ['7'] = 59,
|
['0'] = 52, ['1'] = 53, ['2'] = 54, ['3'] = 55, ['4'] = 56, ['5'] = 57, ['6'] = 58, ['7'] = 59,
|
||||||
['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!
|
||||||
@@ -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++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4334,22 +4337,25 @@ 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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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] == ' ')
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
403
src/rtext.c
403
src/rtext.c
@@ -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);
|
if ((codepoints != NULL) && (length > 0))
|
||||||
const char *utf8 = NULL;
|
|
||||||
int size = 0;
|
|
||||||
|
|
||||||
for (int i = 0, bytes = 0; i < length; i++)
|
|
||||||
{
|
{
|
||||||
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;
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -1125,17 +1125,19 @@ Image GenImageCellular(int width, int height, int tileSize)
|
|||||||
Image GenImageText(int width, int height, const char *text)
|
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)
|
||||||
|
if (text == NULL) return imText;
|
||||||
|
|
||||||
int size = (int)strlen(text); // Get size in bytes of text
|
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')
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user