29 Commits

Author SHA1 Message Date
Ray
f00317bead Added required library 2026-07-11 17:01:09 +02:00
Ray
8805ab3ee2 Update rmodels.c 2026-07-11 16:57:49 +02:00
Ray
ebec978443 REVIEWED: TextJoin(), TextSubtext(), TextInsert*() - ROS: CLN-018 2026-07-11 16:45:56 +02:00
Ray
cfe56d97ea REVIEWED: GetPixelDataSize(), avoid allocations >2GB - ROS: CLN-013
REVIEWED: `GenImageColor()`, `ImageRotate()`
2026-07-11 16:42:57 +02:00
Ray
97d2b0146c REVIEWED: Replace assert() by runtime checks - ROS: CLN-012 2026-07-11 16:22:05 +02:00
Ray
c31666fe2a REVIEWED: ImageFromImage() - ROS: CLN-007 2026-07-11 16:20:41 +02:00
Ray
ab35e0f712 REVIEWED: LoadAutomationEventList() - ROS: CLN-005, CLN-006 2026-07-11 16:18:34 +02:00
Ray
c85cd07db0 Update rmodels.c 2026-07-11 12:22:03 +02:00
Ray
f458296a07 small optimization 2026-07-11 12:21:24 +02:00
Ray
8df2f13962 Update raudio.c 2026-07-11 12:20:57 +02:00
cba8f4286d Fix initial macOS window size (#5967) 2026-07-11 12:06:54 +02:00
86c7edfd74 [raudio] Fix memory leak when loading .wav files (#5963)
* [raudio] Fix memory leak when loading .wav files

* [change] Insert a space.
2026-07-11 12:03:41 +02:00
Ray
caadb48e25 Update SECURITY.md 2026-07-07 15:54:57 +02:00
Ray
3e9fb4757f Update rcore_drm.c 2026-07-07 12:17:23 +02:00
d159739a8b Fix DRM/EGL segfault on some NVIDIA proprietary drivers (#5960) 2026-07-07 12:15:54 +02:00
Ray
f20f61d754 Merge branch 'master' of https://github.com/raysan5/raylib 2026-07-07 11:47:31 +02:00
Ray
81b740403a Updated 2026-07-07 11:47:19 +02:00
3cb8a3d782 ReiLua Lua binding update to raylib 6.0. (#5959) 2026-07-07 11:46:07 +02:00
Ray
f8e42cb509 REVIEWED: OpenURL(), added some safety checks to mitigate possible malicious url inputs 2026-07-07 11:45:30 +02:00
Ray
d631a5a466 Merge branch 'master' of https://github.com/raysan5/raylib 2026-07-06 19:14:22 +02:00
Ray
5992929e56 REVIEWED: Security: strncpy() replaced by snprintf() 2026-07-06 19:13:44 +02:00
3d640698d8 fix UB in ComputeSHA-1 (#5957) 2026-07-06 18:35:02 +02:00
088177b269 rlparser: update raylib_api.* by CI 2026-07-06 16:33:13 +00:00
2e03c9c266 Change boneCount field type from int to unsigned int (#5955) 2026-07-06 18:33:01 +02:00
Ray
65abee1cba Updated GA setup-emsdk action 2026-07-04 12:25:06 +02:00
Ray
1911a2827b REVIEWED: GitHub Actuions update to latest actions 2026-07-03 23:09:19 +02:00
Ray
bb96b920d3 Update rtextures.c 2026-07-03 11:31:45 +02:00
350124a45b [rcore] Fix SDL3 backend using wrong joystick functions (#5948)
* [rcore] Fix gamepad code using outdated functions

* [rcore] Remove printing available joysticks
2026-07-03 01:00:51 +02:00
Ray
5e374218ee REVIEWED: Set mouse position to (0,0) when mouse leaves window area #5945 2026-07-02 18:58:01 +02:00
36 changed files with 440 additions and 302 deletions

View File

@ -44,7 +44,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@master
with:
submodules: recursive
@ -128,7 +128,7 @@ jobs:
- name: Upload CodeQL results as an artifact
if: success() || failure()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: codeql-results
path: ${{ steps.step1.outputs.sarif-output }}

View File

@ -21,7 +21,7 @@ permissions:
jobs:
build:
permissions:
contents: write # for actions/upload-release-asset to upload release asset
contents: write # Required for actions/upload-release-asset to upload release asset
runs-on: windows-latest
strategy:
fail-fast: false
@ -82,7 +82,7 @@ jobs:
tar -czvf ${{ env.RELEASE_NAME }}.tar.gz ${{ env.RELEASE_NAME }}
- name: Upload Artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: ${{ env.RELEASE_NAME }}
path: |
@ -90,7 +90,7 @@ jobs:
!./build/${{ env.RELEASE_NAME }}.tar.gz
- name: Upload Artifact to Release
uses: softprops/action-gh-release@v1
uses: softprops/action-gh-release@v3
with:
files: ./build/${{ env.RELEASE_NAME }}.tar.gz
env:

View File

@ -36,7 +36,7 @@ jobs:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@master
- name: Create Build Environment
# Some projects don't allow in-source building, so create a separate build directory
@ -75,7 +75,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@master
- name: Create Build Environment
# Some projects don't allow in-source building, so create a separate build directory

View File

@ -22,7 +22,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@master
- name: Setup Environment
run: |

View File

@ -22,7 +22,7 @@ jobs:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@master
- name: Add MSBuild to PATH
uses: microsoft/setup-msbuild@v1

View File

@ -21,7 +21,7 @@ permissions:
jobs:
build:
permissions:
contents: write # for actions/upload-release-asset to upload release asset
contents: write # Required for actions/upload-release-asset to upload release asset
strategy:
fail-fast: false
max-parallel: 1
@ -112,7 +112,7 @@ jobs:
tar -czvf ${{ env.RELEASE_NAME }}.tar.gz ${{ env.RELEASE_NAME }}
- name: Upload Artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: ${{ env.RELEASE_NAME }}
path: |
@ -120,7 +120,7 @@ jobs:
!./build/${{ env.RELEASE_NAME }}.tar.gz
- name: Upload Artifact to Release
uses: softprops/action-gh-release@v1
uses: softprops/action-gh-release@v3
with:
files: ./build/${{ env.RELEASE_NAME }}.tar.gz
env:

View File

@ -21,7 +21,7 @@ permissions:
jobs:
build:
permissions:
contents: write # for actions/upload-release-asset to upload release asset
contents: write # Required for actions/upload-release-asset to upload release asset
runs-on: macos-latest
env:
@ -99,7 +99,7 @@ jobs:
tar -czvf ${{ env.RELEASE_NAME }}.tar.gz ${{ env.RELEASE_NAME }}
- name: Upload Artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: ${{ env.RELEASE_NAME }}
path: |
@ -107,7 +107,7 @@ jobs:
!./build/${{ env.RELEASE_NAME }}.tar.gz
- name: Upload Artifact to Release
uses: softprops/action-gh-release@v1
uses: softprops/action-gh-release@v3
with:
files: ./build/${{ env.RELEASE_NAME }}.tar.gz
env:

View File

@ -27,7 +27,7 @@ jobs:
uses: actions/checkout@master
- name: Setup emsdk
uses: mymindstorm/setup-emsdk@v14
uses: emscripten-core/setup-emsdk@v15
with:
version: 5.0.3
actions-cache-folder: 'emsdk-cache'
@ -69,7 +69,7 @@ jobs:
shell: cmd
- name: Upload Artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: ${{ env.RELEASE_NAME }}
path: |
@ -77,7 +77,7 @@ jobs:
!./build/${{ env.RELEASE_NAME }}.zip
- name: Upload Artifact to Release
uses: softprops/action-gh-release@v1
uses: softprops/action-gh-release@v3
with:
files: ./build/${{ env.RELEASE_NAME }}.zip
env:

View File

@ -21,7 +21,7 @@ permissions:
jobs:
build:
permissions:
contents: write # for actions/upload-release-asset to upload release asset
contents: write # Required for actions/upload-release-asset to upload release asset
runs-on: windows-latest
strategy:
fail-fast: false
@ -81,7 +81,7 @@ jobs:
# Setup MSBuild.exe path if required
- name: Setup MSBuild
uses: microsoft/setup-msbuild@v2
uses: microsoft/setup-msbuild@v3
if: matrix.compiler == 'msvc16'
- name: Build Library (MinGW-w64 32bit)
@ -140,7 +140,7 @@ jobs:
shell: cmd
- name: Upload Artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: ${{ env.RELEASE_NAME }}
path: |
@ -148,7 +148,7 @@ jobs:
!./build/${{ env.RELEASE_NAME }}.zip
- name: Upload Artifact to Release
uses: softprops/action-gh-release@v1
uses: softprops/action-gh-release@v3
with:
files: ./build/${{ env.RELEASE_NAME }}.zip
env:

View File

@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@master
- name: Update parse files
working-directory: tools/rlparser

View File

@ -15,10 +15,10 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@master
- name: Setup emsdk
uses: mymindstorm/setup-emsdk@v14
uses: emscripten-core/setup-emsdk@v15
with:
version: 'latest'
actions-cache-folder: 'emsdk-cache'

View File

@ -52,7 +52,7 @@ Some people ported raylib to other languages in the form of bindings or wrappers
| [KaylibKit](https://codeberg.org/Kenta/KaylibKit) | 4.5 | [Kotlin/native](https://kotlinlang.org) | Zlib |
| [raylib-lua](https://github.com/TSnake41/raylib-lua) | 5.5 | [Lua](http://www.lua.org) | ISC |
| [raylib-lua-bindings (WIP)](https://github.com/legendaryredfox/raylib-lua-bindings) | 5.5 | [Lua](http://www.lua.org) | ISC |
| [ReiLua](https://github.com/nullstare/ReiLua) | 5.5 | [Lua](http://www.lua.org) | MIT |
| [ReiLua](https://github.com/nullstare/ReiLua) | 6.0 | [Lua](http://www.lua.org) | MIT |
| [raylib-lua-sol](https://github.com/RobLoach/raylib-lua-sol) | 5.5 | [Lua](http://www.lua.org) | Zlib |
| [raylib-luajit](https://github.com/homma/raylib-luajit) | 5.5 | [Lua](http://www.lua.org) | MIT |
| [raylib-luajit-generated](https://github.com/james2doyle/raylib-luajit-generated) | 5.5 | [Lua](http://www.lua.org) | MIT |

View File

@ -11,8 +11,4 @@ Most considerations of errors and defects can be handled using the project Issue
## Reporting a Vulnerability
Discovered vulnerability can be directly reported using the project Issues and/or Discussions.
_TODO: Tell them where to go, how often they can expect to get an update on a
reported vulnerability, what to expect if the vulnerability is accepted or
declined, etc._
Discovered vulnerability can be directly reported using the project Issues and/or Discussions. They will be reviewed as soon as possible.

View File

@ -1020,7 +1020,12 @@ static int parseLine(Command *command, const char *p, unsigned int p_len,
int triangulate) {
char linebuf[4096];
const char *token;
assert(p_len < 4095);
// @raysan5:
// WARNING: If -DNDEBUG is set when compiling, assertions will not
// be present in the compiled binary, replacing by a runtime check
//assert(p_len < 4095);
if (p_len > 4095) return 0;
memcpy(linebuf, p, p_len);
linebuf[p_len] = '\0';
@ -1102,8 +1107,12 @@ static int parseLine(Command *command, const char *p, unsigned int p_len,
tinyobj_vertex_index_t i1;
tinyobj_vertex_index_t i2 = f[1];
assert(3 * num_f < TINYOBJ_MAX_FACES_PER_F_LINE);
// @raysan5:
// WARNING: If -DNDEBUG is set when compiling, assertions will not
// be present in the compiled binary, replacing by a runtime check
//assert(3 * num_f < TINYOBJ_MAX_FACES_PER_F_LINE);
if (3*num_f > TINYOBJ_MAX_FACES_PER_F_LINE) return 0;
for (k = 2; k < num_f; k++) {
i1 = i2;
i2 = f[k];
@ -1119,7 +1128,13 @@ static int parseLine(Command *command, const char *p, unsigned int p_len,
} else {
unsigned int k = 0;
assert(num_f < TINYOBJ_MAX_FACES_PER_F_LINE);
// @raysan5:
// WARNING: If -DNDEBUG is set when compiling, assertions will not
// be present in the compiled binary, replacing by a runtime check
//assert(num_f < TINYOBJ_MAX_FACES_PER_F_LINE);
if (num_f > TINYOBJ_MAX_FACES_PER_F_LINE) return 0;
for (k = 0; k < num_f; k++) {
command->f[k] = f[k];
}

View File

@ -663,13 +663,22 @@ double GetTime(void)
}
// Open URL with default system browser (if available)
// NOTE: This function is only safe to use if the provided URL is safe
// A user could craft a malicious string performing another action
// Avoid calling this function with user input non-validated strings
// WARNING: This function is only safe to use if you control the URL given,
// a user could craft a malicious string to perform and undesired action
// NOTE: Some safety checks have been added to mitigate security issues
void OpenURL(const char *url)
{
// Security check to (partially) avoid malicious code
if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character");
if ((strchr(url, '\'') != NULL) || (strchr(url, '\"') != NULL))
{
// Filter characters: ' and "
TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'\"] characters");
}
else if ((strncmp(url, "http://", 7) != 0) && (strncmp(url, "https://", 8) != 0))
{
// Only allow URL starting with "http://" or "https://" protocols
TRACELOG(LOG_WARNING, "SYSTEM: Provided URL must start with 'http://' or 'https://' protocols");
}
else
{
JNIEnv *env = NULL;

View File

@ -151,7 +151,7 @@ static void CharCallback(GLFWwindow *window, unsigned int codepoint);
static void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods); // GLFW3 Mouse Button Callback, runs on mouse button pressed
static void MouseCursorPosCallback(GLFWwindow *window, double x, double y); // GLFW3 Cursor Position Callback, runs on mouse move
static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffset); // GLFW3 Scrolling Callback, runs on mouse wheel
static void CursorEnterCallback(GLFWwindow *window, int enter); // GLFW3 Cursor Enter Callback, cursor enters client area
static void CursorEnterCallback(GLFWwindow *window, int entered); // GLFW3 Cursor Enter Callback, cursor enters client area
static void JoystickCallback(int jid, int event); // GLFW3 Joystick Connected/Disconnected Callback
// Memory allocator wrappers [used by glfwInitAllocator()]
@ -1181,17 +1181,25 @@ double GetTime(void)
}
// Open URL with default system browser (if available)
// NOTE: This function is only safe to use if you control the URL given
// A user could craft a malicious string performing another action
// Only call this function yourself not with user input or make sure to check the string yourself
// REF: https://github.com/raysan5/raylib/issues/686
// WARNING: This function is only safe to use if you control the URL given,
// a user could craft a malicious string to perform and undesired action
// NOTE: Some safety checks have been added to mitigate security issues
void OpenURL(const char *url)
{
// Security check to (partially) avoid malicious code
if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character");
else
if ((strchr(url, '\'') != NULL) || (strchr(url, '\"') != NULL))
{
char *cmd = (char *)RL_CALLOC(strlen(url) + 32, sizeof(char));
// Filter characters: ' and "
TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'\"] characters");
}
else if ((strncmp(url, "http://", 7) != 0) && (strncmp(url, "https://", 8) != 0))
{
// Only allow URL starting with "http://" or "https://" protocols
TRACELOG(LOG_WARNING, "SYSTEM: Provided URL must start with 'http://' or 'https://' protocols");
}
else
{
char *cmd = (char *)RL_CALLOC(strlen(url) + 16, sizeof(char));
#if defined(_WIN32)
sprintf(cmd, "explorer \"%s\"", url);
#endif
@ -1201,7 +1209,9 @@ void OpenURL(const char *url)
#if defined(__APPLE__)
sprintf(cmd, "open '%s'", url);
#endif
// TODO: Replace system() call by custom process
int result = system(cmd);
if (result == -1) TRACELOG(LOG_WARNING, "OpenURL() child process could not be created");
RL_FREE(cmd);
}
@ -1683,6 +1693,14 @@ int InitPlatform(void)
return -1;
}
#if defined(__APPLE__)
// AppKit can constrain the requested window size to the visible work area during creation
int windowWidth = 0;
int windowHeight = 0;
glfwGetWindowSize(platform.handle, &windowWidth, &windowHeight);
if ((windowWidth > 0) && (windowHeight > 0)) CORE.Window.screen = (Size){ windowWidth, windowHeight };
#endif
// NOTE: Not considering scale factor now, considered below
CORE.Window.render.width = CORE.Window.screen.width;
CORE.Window.render.height = CORE.Window.screen.height;
@ -1829,7 +1847,7 @@ int InitPlatform(void)
{
CORE.Input.Gamepad.ready[i] = true;
CORE.Input.Gamepad.axisCount[i] = GLFW_GAMEPAD_AXIS_LAST + 1;
strncpy(CORE.Input.Gamepad.name[i], glfwGetJoystickName(i), MAX_GAMEPAD_NAME_LENGTH - 1);
snprintf(CORE.Input.Gamepad.name[i], MAX_GAMEPAD_NAME_LENGTH, "%s", glfwGetJoystickName(i));
}
}
//----------------------------------------------------------------------------
@ -2044,7 +2062,7 @@ static void WindowDropCallback(GLFWwindow *window, int count, const char **paths
for (unsigned int i = 0; i < CORE.Window.dropFileCount; i++)
{
CORE.Window.dropFilepaths[i] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char));
strncpy(CORE.Window.dropFilepaths[i], paths[i], MAX_FILEPATH_LENGTH - 1);
snprintf(CORE.Window.dropFilepaths[i], MAX_FILEPATH_LENGTH, "%s", paths[i]);
}
}
}
@ -2167,10 +2185,18 @@ static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffs
}
// GLFW3: Cursor ennter callback, when cursor enters the window
static void CursorEnterCallback(GLFWwindow *window, int enter)
static void CursorEnterCallback(GLFWwindow *window, int entered)
{
if (enter) CORE.Input.Mouse.cursorOnScreen = true;
else CORE.Input.Mouse.cursorOnScreen = false;
if (entered)
{
// NOTE: Mouse position updated by MouseCursorPosCallback()
CORE.Input.Mouse.cursorOnScreen = true;
}
else
{
CORE.Input.Mouse.cursorOnScreen = false;
CORE.Input.Mouse.currentPosition = (Vector2){ 0 };
}
}
// GLFW3: Joystick connected/disconnected callback
@ -2183,7 +2209,7 @@ static void JoystickCallback(int jid, int event)
// WARNING: If glfwGetJoystickName() is longer than MAX_GAMEPAD_NAME_LENGTH,
// only copy up to (MAX_GAMEPAD_NAME_LENGTH -1) to destination string
memset(CORE.Input.Gamepad.name[jid], 0, MAX_GAMEPAD_NAME_LENGTH);
strncpy(CORE.Input.Gamepad.name[jid], glfwGetJoystickName(jid), MAX_GAMEPAD_NAME_LENGTH - 1);
snprintf(CORE.Input.Gamepad.name[jid], MAX_GAMEPAD_NAME_LENGTH, "%s", glfwGetJoystickName(jid));
}
else if (event == GLFW_DISCONNECTED)
{

View File

@ -486,14 +486,14 @@ static void RGFW_cb_dropfunc(const RGFW_event *e)
CORE.Window.dropFilepaths = (char **)RL_CALLOC(1024, sizeof(char *));
CORE.Window.dropFilepaths[CORE.Window.dropFileCount] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char));
strcpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], e->drop.value->data);
snprintf(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], MAX_FILEPATH_LENGTH, "%s", e->drop.value->data);
CORE.Window.dropFileCount++;
}
else if (CORE.Window.dropFileCount < 1024)
{
CORE.Window.dropFilepaths[CORE.Window.dropFileCount] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char));
strcpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], e->drop.value->data);
snprintf(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], MAX_FILEPATH_LENGTH, "%s", e->drop.value->data);
CORE.Window.dropFileCount++;
}
@ -1471,15 +1471,25 @@ double GetTime(void)
}
// Open URL with default system browser (if available)
// NOTE: This function is only safe to use if you control the URL given
// A user could craft a malicious string performing another action
// WARNING: This function is only safe to use if you control the URL given,
// a user could craft a malicious string to perform and undesired action
// NOTE: Some safety checks have been added to mitigate security issues
void OpenURL(const char *url)
{
// Security check to (partially) avoid malicious code on target platform
if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character");
else
// Security check to (partially) avoid malicious code
if ((strchr(url, '\'') != NULL) || (strchr(url, '\"') != NULL))
{
char *cmd = (char *)RL_CALLOC(strlen(url) + 32, sizeof(char));
// Filter characters: ' and "
TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'\"] characters");
}
else if ((strncmp(url, "http://", 7) != 0) && (strncmp(url, "https://", 8) != 0))
{
// Only allow URL starting with "http://" or "https://" protocols
TRACELOG(LOG_WARNING, "SYSTEM: Provided URL must start with 'http://' or 'https://' protocols");
}
else
{
char *cmd = (char *)RL_CALLOC(strlen(url) + 16, sizeof(char));
#if defined(_WIN32)
sprintf(cmd, "explorer \"%s\"", url);
#endif
@ -1609,74 +1619,76 @@ void PollInputEvents(void)
//-----------------------------------------------------------------------------
mg_event gamepad_event;
while (mg_gamepads_check_event(&platform.minigamepad, &gamepad_event)) {
while (mg_gamepads_check_event(&platform.minigamepad, &gamepad_event))
{
int gamepadIndex = gamepad_event.gamepad->index;
switch (gamepad_event.type) {
switch (gamepad_event.type)
{
case MG_EVENT_BUTTON_PRESS:
{
int button = mg_buttonConvertTable[gamepad_event.button];
if (button >= 0)
{
int button = mg_buttonConvertTable[gamepad_event.button];
if (button >= 0)
{
CORE.Input.Gamepad.currentButtonState[gamepadIndex][button] = 1;
CORE.Input.Gamepad.lastButtonPressed = button;
}
} break;
CORE.Input.Gamepad.currentButtonState[gamepadIndex][button] = 1;
CORE.Input.Gamepad.lastButtonPressed = button;
}
} break;
case MG_EVENT_BUTTON_RELEASE:
{
int button = mg_buttonConvertTable[gamepad_event.button];
if (button >= 0)
{
int button = mg_buttonConvertTable[gamepad_event.button];
if (button >= 0)
{
CORE.Input.Gamepad.currentButtonState[gamepadIndex][button] = 0;
if (CORE.Input.Gamepad.lastButtonPressed == button) CORE.Input.Gamepad.lastButtonPressed = 0;
}
} break;
CORE.Input.Gamepad.currentButtonState[gamepadIndex][button] = 0;
if (CORE.Input.Gamepad.lastButtonPressed == button) CORE.Input.Gamepad.lastButtonPressed = 0;
}
} break;
case MG_EVENT_AXIS_MOVE:
{
int axis = mg_axisConvertTable[gamepad_event.axis];
{
int axis = mg_axisConvertTable[gamepad_event.axis];
switch (axis) {
case GAMEPAD_AXIS_LEFT_X:
case GAMEPAD_AXIS_LEFT_Y:
case GAMEPAD_AXIS_RIGHT_X:
case GAMEPAD_AXIS_RIGHT_Y:
CORE.Input.Gamepad.axisState[gamepadIndex][axis] = platform.minigamepad.gamepads[gamepadIndex].axes[gamepad_event.axis].value;
break;
case GAMEPAD_AXIS_LEFT_TRIGGER:
case GAMEPAD_AXIS_RIGHT_TRIGGER:
CORE.Input.Gamepad.axisState[gamepadIndex][axis] = platform.minigamepad.gamepads[gamepadIndex].axes[gamepad_event.axis].value;
switch (axis) {
case GAMEPAD_AXIS_LEFT_X:
case GAMEPAD_AXIS_LEFT_Y:
case GAMEPAD_AXIS_RIGHT_X:
case GAMEPAD_AXIS_RIGHT_Y:
{
CORE.Input.Gamepad.axisState[gamepadIndex][axis] = platform.minigamepad.gamepads[gamepadIndex].axes[gamepad_event.axis].value;
} break;
case GAMEPAD_AXIS_LEFT_TRIGGER:
case GAMEPAD_AXIS_RIGHT_TRIGGER:
{
CORE.Input.Gamepad.axisState[gamepadIndex][axis] = platform.minigamepad.gamepads[gamepadIndex].axes[gamepad_event.axis].value;
// Trigger button press when axis is all the way
int button = (axis == GAMEPAD_AXIS_LEFT_TRIGGER) ? GAMEPAD_BUTTON_LEFT_TRIGGER_2 : GAMEPAD_BUTTON_RIGHT_TRIGGER_2;
int pressed = (platform.minigamepad.gamepads[gamepadIndex].axes[gamepad_event.axis].value >= 1.0f);
// Trigger button press when axis is all the way
int button = (axis == GAMEPAD_AXIS_LEFT_TRIGGER) ? GAMEPAD_BUTTON_LEFT_TRIGGER_2 : GAMEPAD_BUTTON_RIGHT_TRIGGER_2;
int pressed = (platform.minigamepad.gamepads[gamepadIndex].axes[gamepad_event.axis].value >= 1.0f);
CORE.Input.Gamepad.currentButtonState[gamepadIndex][button] = pressed;
if (pressed) CORE.Input.Gamepad.lastButtonPressed = button;
else if (CORE.Input.Gamepad.lastButtonPressed == button) CORE.Input.Gamepad.lastButtonPressed = 0;
break;
}
} break;
CORE.Input.Gamepad.currentButtonState[gamepadIndex][button] = pressed;
if (pressed) CORE.Input.Gamepad.lastButtonPressed = button;
else if (CORE.Input.Gamepad.lastButtonPressed == button) CORE.Input.Gamepad.lastButtonPressed = 0;
} break;
default: break;
}
} break;
case MG_EVENT_GAMEPAD_CONNECT:
{
CORE.Input.Gamepad.ready[gamepadIndex] = true;
CORE.Input.Gamepad.axisState[gamepadIndex][GAMEPAD_AXIS_LEFT_TRIGGER] = -1.0f;
CORE.Input.Gamepad.axisState[gamepadIndex][GAMEPAD_AXIS_RIGHT_TRIGGER] = -1.0f;
int axisCount = 0;
for (int i = 0; i < MG_AXIS_COUNT; i += 1) {
if (platform.minigamepad.gamepads[gamepadIndex].axes[i].supported)
{
axisCount += 1;
}
else
{
break;
}
for (int i = 0; i < MG_AXIS_COUNT; i += 1)
{
if (platform.minigamepad.gamepads[gamepadIndex].axes[i].supported) axisCount += 1;
else break;
}
CORE.Input.Gamepad.axisCount[gamepadIndex] = axisCount;
strcpy(CORE.Input.Gamepad.name[gamepadIndex], platform.minigamepad.gamepads[gamepadIndex].name);
break;
case MG_EVENT_GAMEPAD_DISCONNECT:
CORE.Input.Gamepad.ready[gamepadIndex] = false;
break;
snprintf(CORE.Input.Gamepad.name[gamepadIndex], MAX_GAMEPAD_NAME_LENGTH, "%s", platform.minigamepad.gamepads[gamepadIndex].name);
} break;
case MG_EVENT_GAMEPAD_DISCONNECT: CORE.Input.Gamepad.ready[gamepadIndex] = false; break;
default: break;
}
}

View File

@ -1341,14 +1341,22 @@ double GetTime(void)
}
// Open URL with default system browser (if available)
// NOTE: This function is only safe to use if the provided URL is safe
// A user could craft a malicious string performing another action
// Avoid calling this function with user input non-validated strings
// REF: https://github.com/raysan5/raylib/issues/686
// WARNING: This function is only safe to use if you control the URL given,
// a user could craft a malicious string to perform and undesired action
// NOTE: Some safety checks have been added to mitigate security issues
void OpenURL(const char *url)
{
// Security check to (partially) avoid malicious code
if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character");
if ((strchr(url, '\'') != NULL) || (strchr(url, '\"') != NULL))
{
// Filter characters: ' and "
TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'\"] characters");
}
else if ((strncmp(url, "http://", 7) != 0) && (strncmp(url, "https://", 8) != 0))
{
// Only allow URL starting with "http://" or "https://" protocols
TRACELOG(LOG_WARNING, "SYSTEM: Provided URL must start with 'http://' or 'https://' protocols");
}
else SDL_OpenURL(url);
}
@ -1512,9 +1520,9 @@ void PollInputEvents(void)
// Event memory is now managed by SDL, so it should not be freed in SDL_EVENT_DROP_FILE,
// in case data needs to be hold onto the text in SDL_EVENT_TEXT_EDITING and SDL_EVENT_TEXT_INPUT events,
// a copy is required, SDL_TEXTINPUTEVENT_TEXT_SIZE is no longer necessary and has been removed
strncpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event.drop.data, MAX_FILEPATH_LENGTH - 1);
snprintf(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], MAX_FILEPATH_LENGTH, "%s", event.drop.data);
#else
strncpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event.drop.file, MAX_FILEPATH_LENGTH - 1);
snprintf(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], MAX_FILEPATH_LENGTH, "%s", event.drop.file);
SDL_free(event.drop.file);
#endif
@ -1525,9 +1533,9 @@ void PollInputEvents(void)
CORE.Window.dropFilepaths[CORE.Window.dropFileCount] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char));
#if defined(USING_VERSION_SDL3)
strncpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event.drop.data, MAX_FILEPATH_LENGTH - 1);
snprintf(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], MAX_FILEPATH_LENGTH, "%s", event.drop.data);
#else
strncpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event.drop.file, MAX_FILEPATH_LENGTH - 1);
snprintf(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], MAX_FILEPATH_LENGTH, "%s", event.drop.file);
SDL_free(event.drop.file);
#endif
@ -1812,8 +1820,8 @@ void PollInputEvents(void)
CORE.Input.Gamepad.axisState[nextAvailableSlot][GAMEPAD_AXIS_RIGHT_TRIGGER] = -1.0f;
memset(CORE.Input.Gamepad.name[nextAvailableSlot], 0, MAX_GAMEPAD_NAME_LENGTH);
const char *controllerName = SDL_GameControllerNameForIndex(nextAvailableSlot);
if (controllerName != NULL) strncpy(CORE.Input.Gamepad.name[nextAvailableSlot], controllerName, MAX_GAMEPAD_NAME_LENGTH - 1);
else strncpy(CORE.Input.Gamepad.name[nextAvailableSlot], "noname", 6);
if (controllerName != NULL) snprintf(CORE.Input.Gamepad.name[nextAvailableSlot], MAX_GAMEPAD_NAME_LENGTH, "%s", controllerName);
else memcpy(CORE.Input.Gamepad.name[nextAvailableSlot], "noname", 6);
}
else TRACELOG(LOG_WARNING, "PLATFORM: Unable to open game controller [ERROR: %s]", SDL_GetError());
}
@ -2164,11 +2172,20 @@ int InitPlatform(void)
platform.gamepadId[i] = -1; // Set all gamepad initial instance ids as invalid to not conflict with instance id zero
}
#if defined(USING_VERSION_SDL3)
int numJoysticks;
SDL_JoystickID *joysticks = SDL_GetJoysticks(&numJoysticks);
#else
int numJoysticks = SDL_NumJoysticks();
#endif
for (int i = 0; (i < numJoysticks) && (i < MAX_GAMEPADS); i++)
{
#if defined(USING_VERSION_SDL3)
platform.gamepad[i] = SDL_OpenGamepad(joysticks[i]);
#else
platform.gamepad[i] = SDL_GameControllerOpen(i);
#endif
platform.gamepadId[i] = SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(platform.gamepad[i]));
if (platform.gamepad[i])
@ -2177,7 +2194,12 @@ int InitPlatform(void)
CORE.Input.Gamepad.axisCount[i] = SDL_JoystickNumAxes(SDL_GameControllerGetJoystick(platform.gamepad[i]));
CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_LEFT_TRIGGER] = -1.0f;
CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_RIGHT_TRIGGER] = -1.0f;
strncpy(CORE.Input.Gamepad.name[i], SDL_GameControllerNameForIndex(i), MAX_GAMEPAD_NAME_LENGTH - 1);
#if defined(USING_VERSION_SDL3)
const char *joystickName = SDL_GetJoystickNameForID(joysticks[i]);
#else
const char *joystickName = SDL_GameControllerNameForIndex(i);
#endif
snprintf(CORE.Input.Gamepad.name[i], MAX_GAMEPAD_NAME_LENGTH, "%s", joystickName);
CORE.Input.Gamepad.name[i][MAX_GAMEPAD_NAME_LENGTH - 1] = '\0';
}
else TRACELOG(LOG_WARNING, "PLATFORM: Unable to open game controller [ERROR: %s]", SDL_GetError());

View File

@ -1247,19 +1247,28 @@ double GetTime(void)
}
// Open URL with default system browser (if available)
// NOTE: This function is only safe to use if the provided URL is safe
// A user could craft a malicious string performing another action
// Avoid calling this function with user input non-validated strings
// REF: https://github.com/raysan5/raylib/issues/686
// WARNING: This function is only safe to use if you control the URL given,
// a user could craft a malicious string to perform and undesired action
// NOTE: Some safety checks have been added to mitigate security issues
void OpenURL(const char *url)
{
// Security check to (partially) avoid malicious code on target platform
if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character");
else
// Security check to (partially) avoid malicious code
if ((strchr(url, '\'') != NULL) || (strchr(url, '\"') != NULL))
{
int len = strlen(url) + 32;
// Filter characters: ' and "
TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'\"] characters");
}
else if ((strncmp(url, "http://", 7) != 0) && (strncmp(url, "https://", 8) != 0))
{
// Only allow URL starting with "http://" or "https://" protocols
TRACELOG(LOG_WARNING, "SYSTEM: Provided URL must start with 'http://' or 'https://' protocols");
}
else
{
int len = strlen(url) + 16;
char *cmd = (char *)RL_CALLOC(len, sizeof(char));
snprintf(cmd, len, "explorer \"%s\"", url);
int result = system(cmd);
if (result == -1) TRACELOG(LOG_WARNING, "OpenURL() child process could not be created");
RL_FREE(cmd);

View File

@ -1014,9 +1014,8 @@ double GetTime(void)
}
// Open URL with default system browser (if available)
// NOTE: This function is only safe to use if the provided URL is safe
// A user could craft a malicious string performing another action
// Avoid calling this function with user input non-validated strings
// WARNING: This function is only safe to use if you control the URL given,
// a user could craft a malicious string to perform and undesired action
void OpenURL(const char *url)
{
TRACELOG(LOG_WARNING, "OpenURL() not implemented on target platform");
@ -1446,7 +1445,9 @@ int InitPlatform(void)
return -1;
}
if (!eglChooseConfig(platform.device, NULL, NULL, 0, &numConfigs))
// WARNING: Providing framebufferAttribs is not logically necessary,
// but it may prevent segfaults on some nvidia drivers
if (!eglChooseConfig(platform.device, framebufferAttribs, NULL, 0, &numConfigs))
{
TRACELOG(LOG_WARNING, "DISPLAY: Failed to get EGL config count: 0x%x", eglGetError());
return -1;
@ -2732,4 +2733,4 @@ static void SetupFramebuffer(int width, int height)
}
}
// EOF
// EOF

View File

@ -352,14 +352,22 @@ double GetTime(void)
}
// Open URL with default system browser (if available)
// NOTE: This function is only safe to use if you control the URL given.
// A user could craft a malicious string performing another action.
// Only call this function yourself not with user input or make sure to check the string yourself.
// Ref: https://github.com/raysan5/raylib/issues/686
// WARNING: This function is only safe to use if you control the URL given,
// a user could craft a malicious string to perform and undesired action
// NOTE: Some safety checks have been added to mitigate security issues
void OpenURL(const char *url)
{
// Security check to (partially) avoid malicious code on target platform
if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character");
// Security check to (partially) avoid malicious code
if ((strchr(url, '\'') != NULL) || (strchr(url, '\"') != NULL))
{
// Filter characters: ' and "
TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'\"] characters");
}
else if ((strncmp(url, "http://", 7) != 0) && (strncmp(url, "https://", 8) != 0))
{
// Only allow URL starting with "http://" or "https://" protocols
TRACELOG(LOG_WARNING, "SYSTEM: Provided URL must start with 'http://' or 'https://' protocols");
}
else
{
// TODO: Load url using default browser

View File

@ -1579,7 +1579,7 @@ static void WindowDropCallback(GLFWwindow *window, int count, const char **paths
for (unsigned int i = 0; i < CORE.Window.dropFileCount; i++)
{
CORE.Window.dropFilepaths[i] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char));
strncpy(CORE.Window.dropFilepaths[i], paths[i], MAX_FILEPATH_LENGTH - 1);
snprintf(CORE.Window.dropFilepaths[i], MAX_FILEPATH_LENGTH, "%s", paths[i]);
}
}
}

View File

@ -968,13 +968,22 @@ double GetTime(void)
}
// Open URL with default system browser (if available)
// NOTE: This function is only safe to use if the provided URL is safe
// A user could craft a malicious string performing another action
// Avoid calling this function with user input non-validated strings
// WARNING: This function is only safe to use if you control the URL given,
// a user could craft a malicious string to perform and undesired action
// NOTE: Some safety checks have been added to mitigate security issues
void OpenURL(const char *url)
{
// Security check to (partially) avoid malicious code on target platform
if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character");
// Security check to (partially) avoid malicious code
if ((strchr(url, '\'') != NULL) || (strchr(url, '\"') != NULL))
{
// Filter characters: ' and "
TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'\"] characters");
}
else if ((strncmp(url, "http://", 7) != 0) && (strncmp(url, "https://", 8) != 0))
{
// Only allow URL starting with "http://" or "https://" protocols
TRACELOG(LOG_WARNING, "SYSTEM: Provided URL must start with 'http://' or 'https://' protocols");
}
else emscripten_run_script(TextFormat("window.open('%s', '_blank')", url));
}
@ -1439,7 +1448,7 @@ static void WindowDropCallback(GLFWwindow *window, int count, const char **paths
for (unsigned int i = 0; i < CORE.Window.dropFileCount; i++)
{
CORE.Window.dropFilepaths[i] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char));
strncpy(CORE.Window.dropFilepaths[i], paths[i], MAX_FILEPATH_LENGTH - 1);
snprintf(CORE.Window.dropFilepaths[i], MAX_FILEPATH_LENGTH, "%s", paths[i]);
}
}
}

View File

@ -822,7 +822,7 @@ Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int
wave.sampleRate = wav.sampleRate;
wave.sampleSize = 16;
wave.channels = wav.channels;
wave.data = (short *)RL_MALLOC((size_t)wave.frameCount*wave.channels*sizeof(short));
wave.data = (short *)RL_CALLOC((size_t)wave.frameCount*wave.channels, sizeof(short));
// NOTE: Forcing conversion to 16bit sample size on reading
drwav_read_pcm_frames_s16(&wav, wave.frameCount, (drwav_int16 *)wave.data);
@ -845,7 +845,7 @@ Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int
wave.sampleSize = 16; // By default, ogg data is 16 bit per sample (short)
wave.channels = info.channels;
wave.frameCount = (unsigned int)stb_vorbis_stream_length_in_samples(oggData); // NOTE: It returns frames!
wave.data = (short *)RL_MALLOC(wave.frameCount*wave.channels*sizeof(short));
wave.data = (short *)RL_CALLOC(wave.frameCount*wave.channels, sizeof(short));
// NOTE: Get the number of samples to process (be careful! asking for number of shorts, not bytes!)
stb_vorbis_get_samples_short_interleaved(oggData, info.channels, (short *)wave.data, wave.frameCount*wave.channels);
@ -1154,7 +1154,7 @@ bool ExportWaveAsCode(Wave wave, const char *fileName)
// Get file name from path and convert variable name to uppercase
char varFileName[256] = { 0 };
strncpy(varFileName, GetFileNameWithoutExt(fileName), 256 - 1);
snprintf(varFileName, 256, "%s", GetFileNameWithoutExt(fileName)); // 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; }
// Add wave information
@ -1258,7 +1258,7 @@ void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels)
return;
}
void *data = RL_MALLOC(frameCount*channels*(sampleSize/8));
void *data = RL_CALLOC(frameCount*channels*(sampleSize/8), 1);
frameCount = (ma_uint32)ma_convert_frames(data, frameCount, formatOut, channels, sampleRate, wave->data, frameCountIn, formatIn, wave->channels, wave->sampleRate);
if (frameCount == 0)
@ -1772,7 +1772,7 @@ void UnloadMusicStream(Music music)
{
if (false) { }
#if SUPPORT_FILEFORMAT_WAV
else if (music.ctxType == MUSIC_AUDIO_WAV) drwav_uninit((drwav *)music.ctxData);
else if (music.ctxType == MUSIC_AUDIO_WAV) { drwav_uninit((drwav *)music.ctxData); RL_FREE(music.ctxData); }
#endif
#if SUPPORT_FILEFORMAT_OGG
else if (music.ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close((stb_vorbis *)music.ctxData);
@ -2827,7 +2827,7 @@ static const char *GetFileNameWithoutExt(const char *filePath)
static char fileName[MAX_FILENAMEWITHOUTEXT_LENGTH] = { 0 };
memset(fileName, 0, MAX_FILENAMEWITHOUTEXT_LENGTH);
if (filePath != NULL) strncpy(fileName, GetFileName(filePath), MAX_FILENAMEWITHOUTEXT_LENGTH - 1); // Get filename with extension
if (filePath != NULL) snprintf(fileName, MAX_FILENAMEWITHOUTEXT_LENGTH, "%s", GetFileName(filePath));
int fileNameLength = (int)strlen(fileName); // Get size in bytes

View File

@ -409,7 +409,7 @@ typedef struct BoneInfo {
// Skeleton, animation bones hierarchy
typedef struct ModelSkeleton {
int boneCount; // Number of bones
unsigned int boneCount; // Number of bones
BoneInfo *bones; // Bones information (skeleton)
ModelAnimPose bindPose; // Bones base transformation (Transform[])
} ModelSkeleton;
@ -436,7 +436,7 @@ typedef struct Model {
typedef struct ModelAnimation {
char name[32]; // Animation name
int boneCount; // Number of bones (per pose)
unsigned int boneCount; // Number of bones (per pose)
int keyframeCount; // Number of animation key frames
ModelAnimPose *keyframePoses; // Animation sequence keyframe poses [keyframe][pose]
} ModelAnimation;

View File

@ -108,7 +108,7 @@
#include <stdlib.h> // Required for: srand(), rand(), exit()
#include <stdio.h> // Required for: FILE, fopen(), fseek(), ftell(), fread(), fwrite(), fprintf(), vprintf(), fclose(), sprintf() [Used in OpenURL()]
#include <string.h> // Required for: strlen(), strncpy(), strcmp(), strrchr(), memset(), strcat()
#include <string.h> // Required for: strlen(), strcmp(), strrchr(), memset(), memcpy(), strcat()
#include <stdarg.h> // Required for: va_list, va_start(), va_end() [Used in TraceLog()]
#include <time.h> // Required for: time() [Used in InitTimer()]
#include <math.h> // Required for: tan() [Used in BeginMode3D()], atan2f() [Used in LoadVrStereoConfig()]
@ -1819,7 +1819,7 @@ void TakeScreenshot(const char *fileName)
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[MAX_FILEPATH_LENGTH] = { 0 };
strncpy(path, TextFormat("%s/%s", CORE.Storage.basePath, fileName), MAX_FILEPATH_LENGTH - 1);
snprintf(path, MAX_FILEPATH_LENGTH, "%s", TextFormat("%s/%s", CORE.Storage.basePath, fileName));
ExportImage(image, path); // WARNING: Module required: rtextures
RL_FREE(imgData);
@ -1844,8 +1844,6 @@ void SetConfigFlags(unsigned int flags)
FLAG_SET(CORE.Window.flags, flags);
}
// void OpenURL(const char *url); // Defined per platform
//----------------------------------------------------------------------------------
// Module Functions Definition: Logging system
//----------------------------------------------------------------------------------
@ -1885,12 +1883,12 @@ void TraceLog(int logType, const char *text, ...)
switch (logType)
{
case LOG_TRACE: strncpy(buffer, "TRACE: ", 8); break;
case LOG_DEBUG: strncpy(buffer, "DEBUG: ", 8); break;
case LOG_INFO: strncpy(buffer, "INFO: ", 7); break;
case LOG_WARNING: strncpy(buffer, "WARNING: ", 10); break;
case LOG_ERROR: strncpy(buffer, "ERROR: ", 8); break;
case LOG_FATAL: strncpy(buffer, "FATAL: ", 8); break;
case LOG_TRACE: memcpy(buffer, "TRACE: ", 7); break;
case LOG_DEBUG: memcpy(buffer, "DEBUG: ", 7); break;
case LOG_INFO: memcpy(buffer, "INFO: ", 6); break;
case LOG_WARNING: memcpy(buffer, "WARNING: ", 9); break;
case LOG_ERROR: memcpy(buffer, "ERROR: ", 7); break;
case LOG_FATAL: memcpy(buffer, "FATAL: ", 7); break;
default: break;
}
@ -2063,7 +2061,7 @@ bool ExportDataAsCode(const unsigned char *data, int dataSize, const char *fileN
// Get file name from path
char varFileName[256] = { 0 };
strncpy(varFileName, GetFileNameWithoutExt(fileName), 256 - 1);
snprintf(varFileName, 256, "%s", GetFileNameWithoutExt(fileName));
for (int i = 0; varFileName[i] != '\0'; i++)
{
// Convert variable name to uppercase
@ -2507,7 +2505,7 @@ const char *GetFileNameWithoutExt(const char *filePath)
if (filePath != NULL)
{
strncpy(fileName, GetFileName(filePath), MAX_FILENAME_LENGTH - 1); // Get filename.ext without path
snprintf(fileName, MAX_FILENAME_LENGTH, "%s", GetFileName(filePath)); // Get filename.ext without path
int fileNameLength = (int)strlen(fileName); // Get size in bytes
for (int i = fileNameLength; i > 0; i--) // Reverse search '.'
@ -2561,7 +2559,6 @@ const char *GetDirectoryPath(const char *filePath)
}
else
{
// NOTE: Be careful, strncpy() is not safe, it does not care about '\0'
char *dirPathPtr = dirPath;
if ((filePath[1] != ':') && (filePath[0] != '\\') && (filePath[0] != '/')) dirPathPtr += 2; // Skip drive letter, "C:"
memcpy(dirPathPtr, filePath, strlen(filePath) - (strlen(lastSlash) - 1));
@ -2579,7 +2576,7 @@ const char *GetPrevDirectoryPath(const char *dirPath)
memset(prevDirPath, 0, MAX_FILEPATH_LENGTH);
int dirPathLength = (int)strlen(dirPath);
if (dirPathLength <= 3) strncpy(prevDirPath, dirPath, MAX_FILEPATH_LENGTH - 1);
if (dirPathLength <= 3) snprintf(prevDirPath, MAX_FILEPATH_LENGTH, "%s", dirPath);
for (int i = (dirPathLength - 1); (i >= 0) && (dirPathLength > 3); i--)
{
@ -2588,7 +2585,7 @@ const char *GetPrevDirectoryPath(const char *dirPath)
// Check for root: "C:\" or "/"
if (((i == 2) && (dirPath[1] ==':')) || (i == 0)) i++;
strncpy(prevDirPath, dirPath, i);
memcpy(prevDirPath, dirPath, i);
break;
}
}
@ -3276,7 +3273,7 @@ unsigned int *ComputeMD5(const unsigned char *data, int dataSize)
memcpy(msg + newDataSize, &bitsLen, 4); // Append the len in bits at the end of the buffer
// Process the message in successive 512-bit chunks for each 512-bit chunk of message
for (int offset = 0; offset < newDataSize; offset += (512/8))
for (int offset = 0; offset < newDataSize; offset += 64) // 512/8
{
// Break chunk into sixteen 32-bit words w[j], 0 <= j <= 15
unsigned int *w = (unsigned int *)(msg + offset);
@ -3373,16 +3370,16 @@ unsigned int *ComputeSHA1(const unsigned char *data, int dataSize)
msg[newDataSize - 8] = (unsigned char)(bitsLen >> 56);
// Process the message in successive 512-bit chunks
for (int offset = 0; offset < newDataSize; offset += (512/8))
for (int offset = 0; offset < newDataSize; offset += 64) // 512/8
{
// Break chunk into sixteen 32-bit words w[j], 0 <= j <= 15
unsigned int w[80] = { 0 };
for (int i = 0; i < 16; i++)
{
w[i] = (msg[offset + (i*4) + 0] << 24) |
(msg[offset + (i*4) + 1] << 16) |
(msg[offset + (i*4) + 2] << 8) |
(msg[offset + (i*4) + 3]);
w[i] = ((unsigned int)msg[offset + (i*4) + 0] << 24) |
((unsigned int)msg[offset + (i*4) + 1] << 16) |
((unsigned int)msg[offset + (i*4) + 2] << 8) |
((unsigned int)msg[offset + (i*4) + 3]);
}
// Message schedule: extend the sixteen 32-bit words into eighty 32-bit words:
@ -3604,10 +3601,14 @@ AutomationEventList LoadAutomationEventList(const char *fileName)
case 'c': sscanf(buffer, "c %i", &list.count); break;
case 'e':
{
sscanf(buffer, "e %d %d %d %d %d %d %[^\n]s", &list.events[counter].frame, &list.events[counter].type,
&list.events[counter].params[0], &list.events[counter].params[1], &list.events[counter].params[2], &list.events[counter].params[3], eventDesc);
if (counter < list.capacity)
{
sscanf(buffer, "e %d %d %d %d %d %d %63[^\n]s", &list.events[counter].frame, &list.events[counter].type,
&list.events[counter].params[0], &list.events[counter].params[1], &list.events[counter].params[2], &list.events[counter].params[3], eventDesc);
counter++;
counter++;
}
else TRACELOG(LOG_WARNING, "AUTOMATION: Event goes beyond automated list capacity (MAX: %i): %s", buffer, list.capacity);
} break;
default: break;
}

View File

@ -847,11 +847,11 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad
#define SW_CALLOC(n,sz) RL_CALLOC(n, sz)
#define SW_REALLOC(ptr, newSz) RL_REALLOC(ptr, newSz)
#define SW_FREE(ptr) RL_FREE(ptr)
#include "external/rlsw.h" // OpenGL 1.1 software implementation
#include "external/rlsw.h" // OpenGL 1.1 software implementation
#else
#if defined(__APPLE__)
#include <OpenGL/gl.h> // OpenGL 1.1 library for OSX
#include <OpenGL/glext.h> // OpenGL extensions library
#include <OpenGL/gl.h> // OpenGL 1.1 library for OSX
#include <OpenGL/glext.h> // OpenGL extensions library
#else
// APIENTRY for OpenGL function pointer declarations is required
#if !defined(APIENTRY)
@ -866,7 +866,7 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad
#define WINGDIAPI __declspec(dllimport)
#endif
#include <GL/gl.h> // OpenGL 1.1 library
#include <GL/gl.h> // OpenGL 1.1 library
#endif
#endif
#endif
@ -907,9 +907,10 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad
#endif
#endif
#include <stdlib.h> // Required for: calloc(), free()
#include <string.h> // Required for: strcmp(), strlen() [Used in rlglInit(), on extensions loading]
#include <math.h> // Required for: sqrtf(), sinf(), cosf(), floor(), log()
#include <stdlib.h> // Required for: calloc(), free()
#include <string.h> // Required for: strcmp(), strlen() [Used in rlglInit(), on extensions loading]
#include <math.h> // Required for: sqrtf(), sinf(), cosf(), floor(), log()
#include <limits.h> // Required for: INT_MAX
//----------------------------------------------------------------------------------
// Defines and Macros
@ -2469,7 +2470,7 @@ void rlLoadExtensions(void *loader)
// NOTE: String duplication rquired because glGetString() returns a const string
int extensionsLength = (int)strlen(extensions); // Get extensions string size in bytes
char *extensionsDup = (char *)RL_CALLOC(extensionsLength + 1, sizeof(char)); // Allocate space for copy with additional EOL byte
strncpy(extensionsDup, extensions, extensionsLength);
snprintf(extensionsDup, extensionsLength, "%s", extensions);
extList[numExt] = extensionsDup;
for (int i = 0; i < extensionsLength; i++)
@ -5236,7 +5237,9 @@ static int rlGetPixelDataSize(int width, int height, int format)
{
int blockWidth = (width + 3)/4;
int blockHeight = (height + 3)/4;
dataSize = blockWidth*blockHeight*8;
unsigned long long dataSizeBytes = blockWidth*blockHeight*8;
if (dataSizeBytes < INT_MAX) dataSize = (int)dataSizeBytes;
} break;
case RL_PIXELFORMAT_COMPRESSED_DXT3_RGBA:
case RL_PIXELFORMAT_COMPRESSED_DXT5_RGBA:
@ -5245,13 +5248,17 @@ static int rlGetPixelDataSize(int width, int height, int format)
{
int blockWidth = (width + 3)/4;
int blockHeight = (height + 3)/4;
dataSize = blockWidth*blockHeight*16;
unsigned long long dataSizeBytes = blockWidth*blockHeight*16;
if (dataSizeBytes < INT_MAX) dataSize = (int)dataSizeBytes;
} break;
case RL_PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA: // 4 bytes per each 4x4 block
{
int blockWidth = (width + 3)/4;
int blockHeight = (height + 3)/4;
dataSize = blockWidth*blockHeight*4;
unsigned long long dataSizeBytes = blockWidth*blockHeight*4;
if (dataSizeBytes < INT_MAX) dataSize = (int)dataSizeBytes;
} break;
default: break;
}
@ -5260,9 +5267,11 @@ static int rlGetPixelDataSize(int width, int height, int format)
if ((format >= RL_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) &&
(format <= RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16))
{
double bytesPerPixel = (double)bpp/8.0;
dataSize = (int)(bytesPerPixel*width*height); // Total data size in bytes
unsigned long long dataSizeBytes = (width*height*bpp) >> 3; // Get size in bytes (dividing by 8)
if (dataSizeBytes < INT_MAX) dataSize = (int)dataSizeBytes;
}
if (dataSize == 0) TRACELOG(LOG_WARNING, "Requested image size is larger than 2GB, it can not be allocated");
return dataSize;
}

View File

@ -50,9 +50,9 @@
#include "rlgl.h" // OpenGL abstraction layer to OpenGL 1.1, 2.1, 3.3+ or ES2
#include "raymath.h" // Required for: Vector3, Quaternion and Matrix functionality
#include <stdio.h> // Required for: sprintf()
#include <stdio.h> // Required for: sprintf(), snprintf()
#include <stdlib.h> // Required for: malloc(), calloc(), free()
#include <string.h> // Required for: memcmp(), strlen(), strncpy()
#include <string.h> // Required for: strlen(), memcmp()
#include <math.h> // Required for: sinf(), cosf(), sqrtf(), fabsf()
#if SUPPORT_FILEFORMAT_OBJ || SUPPORT_FILEFORMAT_MTL
@ -2073,7 +2073,7 @@ bool ExportMeshAsCode(Mesh mesh, const char *fileName)
// Get file name from path and convert variable name to uppercase
char varFileName[256] = { 0 };
strncpy(varFileName, GetFileNameWithoutExt(fileName), 256 - 1); // NOTE: Using function provided by [rcore] module
snprintf(varFileName, 256, "%s", GetFileNameWithoutExt(fileName)); // 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; }
// Add image information
@ -2320,7 +2320,7 @@ void UpdateModelAnimation(Model model, ModelAnimation anim, float frame)
Matrix currentPoseMatrix = { 0 };
// Update all bones and bone matrices of model
for (int boneIndex = 0; boneIndex < model.skeleton.boneCount; boneIndex++)
for (unsigned int boneIndex = 0; boneIndex < model.skeleton.boneCount; boneIndex++)
{
// Compute interpolated pose between current and next frame
// NOTE: Storing animation frame data in model.currentPose
@ -2390,7 +2390,7 @@ void UpdateModelAnimationEx(Model model, ModelAnimation animA, float frameA, Mod
Matrix bindPoseMatrix = { 0 };
Matrix currentPoseMatrix = { 0 };
for (int boneIndex = 0; boneIndex < model.skeleton.boneCount; boneIndex++)
for (unsigned int boneIndex = 0; boneIndex < model.skeleton.boneCount; boneIndex++)
{
// Get frame-interpolation for first animation
Vector3 frameATranslation = Vector3Lerp(
@ -4421,7 +4421,7 @@ static Model LoadOBJ(const char *fileName)
}
char currentDir[MAX_FILEPATH_LENGTH] = { 0 };
strncpy(currentDir, GetWorkingDirectory(), MAX_FILEPATH_LENGTH - 1); // Save current working directory
snprintf(currentDir, MAX_FILEPATH_LENGTH, "%s", GetWorkingDirectory()); // Save current working directory
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);
@ -5013,7 +5013,7 @@ static Model LoadIQM(const char *fileName)
// Initialize runtime animation data: current pose and bone matrices
model.currentPose = (Transform *)RL_CALLOC(model.skeleton.boneCount, sizeof(Transform));
model.boneMatrices = (Matrix *)RL_CALLOC(model.skeleton.boneCount, sizeof(Matrix));
for (int j = 0; j < model.skeleton.boneCount; j++) model.boneMatrices[j] = MatrixIdentity();
for (unsigned int j = 0; j < model.skeleton.boneCount; j++) model.boneMatrices[j] = MatrixIdentity();
UnloadFileData(fileData);
@ -5241,7 +5241,7 @@ static ModelAnimation *LoadModelAnimationsIQM(const char *fileName, int *animCou
// Build frameposes
for (unsigned int frame = 0; frame < anim[a].num_frames; frame++)
{
for (int i = 0; i < animations[a].boneCount; i++)
for (unsigned int i = 0; i < animations[a].boneCount; i++)
{
if (bones[i].parent >= 0)
{
@ -5316,8 +5316,8 @@ static Image LoadImageFromCgltfImage(cgltf_image *cgltfImage, const char *texPat
{
int base64Size = (int)strlen(cgltfImage->uri + i + 1);
while (cgltfImage->uri[i + base64Size] == '=') base64Size--; // Ignore optional paddings
int numberOfEncodedBits = base64Size*6 - (base64Size*6) % 8 ; // Encoded bits minus extra bits, so it becomes a multiple of 8 bits
int outSize = numberOfEncodedBits/8 ; // Actual encoded bytes
int numberOfEncodedBits = base64Size*6 - (base64Size*6)%8; // Encoded bits minus extra bits, so it becomes a multiple of 8 bits
int outSize = numberOfEncodedBits >> 3; // Actual encoded bytes
void *data = NULL;
cgltf_options options = { 0 };
@ -5369,15 +5369,15 @@ static Image LoadImageFromCgltfImage(cgltf_image *cgltfImage, const char *texPat
}
// Load bone info from GLTF skin data
static BoneInfo *LoadBoneInfoGLTF(cgltf_skin skin, int *boneCount)
static BoneInfo *LoadBoneInfoGLTF(cgltf_skin skin, unsigned int *boneCount)
{
*boneCount = (int)skin.joints_count;
*boneCount = (unsigned int)skin.joints_count;
BoneInfo *bones = (BoneInfo *)RL_CALLOC(skin.joints_count, sizeof(BoneInfo));
for (unsigned int i = 0; i < skin.joints_count; i++)
{
cgltf_node node = *skin.joints[i];
if (node.name != NULL) strncpy(bones[i].name, node.name, sizeof(bones[i].name) - 1);
if (node.name != NULL) snprintf(bones[i].name, sizeof(bones[i].name), "%s", node.name);
// Find parent bone index by walking up the node tree past any
// non-joint ancestors (intermediate transform nodes used by some
@ -6144,7 +6144,7 @@ static Model LoadGLTF(const char *fileName)
model.skeleton.bones = LoadBoneInfoGLTF(skin, &model.skeleton.boneCount);
model.skeleton.bindPose = (Transform *)RL_CALLOC(model.skeleton.boneCount, sizeof(Transform));
for (int i = 0; i < model.skeleton.boneCount; i++)
for (unsigned int i = 0; i < model.skeleton.boneCount; i++)
{
Matrix bindMatrix = { 0 };
cgltf_float inverseBindTransform[16] = { 0 };
@ -6309,7 +6309,7 @@ static Model LoadGLTF(const char *fileName)
if ((data->skins_count > 0) && !hasJoints && (node->parent != NULL) && (node->parent->mesh == NULL))
{
int parentBoneId = -1;
for (int joint = 0; joint < model.skeleton.boneCount; joint++)
for (unsigned int joint = 0; joint < model.skeleton.boneCount; joint++)
{
if (data->skins[0].joints[joint] == node->parent)
{
@ -6347,7 +6347,7 @@ static Model LoadGLTF(const char *fileName)
// Initialize runtime animation data: current pose and bone matrices
model.currentPose = (Transform *)RL_CALLOC(model.skeleton.boneCount, sizeof(Transform));
model.boneMatrices = (Matrix *)RL_CALLOC(model.skeleton.boneCount, sizeof(Matrix));
for (int j = 0; j < model.skeleton.boneCount; j++) model.boneMatrices[j] = MatrixIdentity();
for (unsigned int j = 0; j < model.skeleton.boneCount; j++) model.boneMatrices[j] = MatrixIdentity();
//----------------------------------------------------------------------------------------------------
// Free unused allocated memory in case of no bones defined
@ -6660,7 +6660,7 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, int *animCo
animDuration = (time > animDuration)? time : animDuration;
}
if (animData.name != NULL) strncpy(animations[a].name, animData.name, sizeof(animations[a].name) - 1);
if (animData.name != NULL) snprintf(animations[a].name, sizeof(animations[a].name), "%s", animData.name);
animations[a].keyframeCount = (int)(animDuration*GLTF_FRAMERATE) + 1;
animations[a].keyframePoses = (Transform **)RL_CALLOC(animations[a].keyframeCount, sizeof(Transform *));
@ -6670,7 +6670,7 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, int *animCo
animations[a].keyframePoses[j] = (Transform *)RL_CALLOC(animations[a].boneCount, sizeof(Transform));
float time = (float)j / GLTF_FRAMERATE;
for (int k = 0; k < animations[a].boneCount; k++)
for (unsigned int k = 0; k < animations[a].boneCount; k++)
{
Vector3 translation = {skin.joints[k]->translation[0], skin.joints[k]->translation[1], skin.joints[k]->translation[2]};
Quaternion rotation = {skin.joints[k]->rotation[0], skin.joints[k]->rotation[1], skin.joints[k]->rotation[2], skin.joints[k]->rotation[3]};
@ -7150,7 +7150,7 @@ static Model LoadM3D(const char *fileName)
for (i = 0; i < (int)m3d->numbone; i++)
{
model.skeleton.bones[i].parent = m3d->bone[i].parent;
strncpy(model.skeleton.bones[i].name, m3d->bone[i].name, sizeof(model.skeleton.bones[i].name) - 1);
snprintf(model.skeleton.bones[i].name, sizeof(model.skeleton.bones[i].name), "%s", m3d->bone[i].name);
model.skeleton.bindPose[i].translation.x = m3d->vertex[m3d->bone[i].pos].x*m3d->scale;
model.skeleton.bindPose[i].translation.y = m3d->vertex[m3d->bone[i].pos].y*m3d->scale;
model.skeleton.bindPose[i].translation.z = m3d->vertex[m3d->bone[i].pos].z*m3d->scale;
@ -7204,7 +7204,7 @@ static Model LoadM3D(const char *fileName)
// Initialize runtime animation data: current pose and bone matrices
model.currentPose = (Transform *)RL_CALLOC(model.skeleton.boneCount, sizeof(Transform));
model.boneMatrices = (Matrix *)RL_CALLOC(model.skeleton.boneCount, sizeof(Matrix));
for (int j = 0; j < model.skeleton.boneCount; j++) model.boneMatrices[j] = MatrixIdentity();
for (unsigned int j = 0; j < model.skeleton.boneCount; j++) model.boneMatrices[j] = MatrixIdentity();
}
m3d_free(m3d);
@ -7258,14 +7258,14 @@ static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, int *animCou
animations[a].keyframeCount = m3d->action[a].durationmsec/M3D_ANIMDELAY;
animations[a].keyframePoses = (Transform **)RL_CALLOC(animations[a].keyframeCount, sizeof(Transform *));
strncpy(animations[a].name, m3d->action[a].name, sizeof(animations[a].name) - 1);
snprintf(animations[a].name, sizeof(animations[a].name), "%s", m3d->action[a].name);
TRACELOG(LOG_INFO, "MODEL: [%s] Loaded animation: %s | Frames: %d | Duration: %fs", fileName, animations[a].name, animations[a].keyframeCount, m3d->action[a].durationmsec);
for (i = 0; i < (int)m3d->numbone; i++)
{
bones[i].parent = m3d->bone[i].parent;
strncpy(bones[i].name, m3d->bone[i].name, sizeof(bones[i].name) - 1);
snprintf(bones[i].name, sizeof(bones[i].name), "%s", m3d->bone[i].name);
}
// A special, never transformed "no bone" bone, used for boneless vertices

View File

@ -58,8 +58,8 @@
#include "rlgl.h" // OpenGL abstraction layer to OpenGL 1.1, 2.1, 3.3+ or ES2 -> Only DrawTextPro()
#include <stdlib.h> // Required for: malloc(), free()
#include <stdio.h> // Required for: vsprintf()
#include <string.h> // Required for: strcmp(), strstr(), strncpy() [Used in TextReplace()], sscanf() [Used in LoadBMFont()]
#include <stdio.h> // Required for: vsprintf(), snprintf()
#include <string.h> // Required for: strcmp(), strstr(), strncpy(), sscanf() [Used in LoadBMFont()]
#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()]
@ -536,7 +536,7 @@ Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int
Font font = { 0 };
char fileExtLower[16] = { 0 };
strncpy(fileExtLower, TextToLower(fileType), 16 - 1);
snprintf(fileExtLower, 16, "%s", TextToLower(fileType));
font.baseSize = fontSize;
font.glyphPadding = 0;
@ -1030,7 +1030,7 @@ bool ExportFontAsCode(Font font, const char *fileName)
// Get file name from path
char fileNamePascal[256] = { 0 };
strncpy(fileNamePascal, TextToPascal(GetFileNameWithoutExt(fileName)), 256 - 1);
snprintf(fileNamePascal, 256, "%s", TextToPascal(GetFileNameWithoutExt(fileName)));
// Get font atlas image and size, required to estimate code file size
// NOTE: This mechanism is highly coupled to raylib
@ -1532,7 +1532,7 @@ char **LoadTextLines(const char *text, int *count)
if ((text[i] == '\n') || (text[i] == '\0'))
{
lines[l] = (char *)RL_CALLOC(lineLen + 1, 1);
strncpy(lines[l], &text[i - lineLen], lineLen);
memcpy(lines[l], &text[i - lineLen], lineLen);
lineLen = 0;
l++;
}
@ -1699,7 +1699,7 @@ const char *TextSubtext(const char *text, int position, int length)
static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH);
if (text != NULL)
if ((text != NULL) && (position > 0) && (length > 0))
{
int textLength = TextLength(text);
@ -1755,8 +1755,8 @@ char *GetTextBetween(const char *text, const char *begin, const char *end)
{
endIndex += (beginIndex + beginLen);
int len = (endIndex - beginIndex - beginLen);
if (len < (MAX_TEXT_BUFFER_LENGTH - 1)) strncpy(buffer, text + beginIndex + beginLen, len);
else strncpy(buffer, text + beginIndex + beginLen, MAX_TEXT_BUFFER_LENGTH - 1);
if (len < (MAX_TEXT_BUFFER_LENGTH - 1)) memcpy(buffer, text + beginIndex + beginLen, len);
else snprintf(buffer, MAX_TEXT_BUFFER_LENGTH, "%s", text + beginIndex + beginLen);
}
}
@ -1764,7 +1764,7 @@ char *GetTextBetween(const char *text, const char *begin, const char *end)
}
// Replace text string
// REQUIRES: strstr(), strncpy()
// REQUIRES: strstr(), memcpy()
// NOTE: Limited text replace functionality, using static string
char *TextReplace(const char *text, const char *search, const char *replacement)
{
@ -1821,7 +1821,7 @@ char *TextReplace(const char *text, const char *search, const char *replacement)
// Copy remaind text part after replacement to result (pointed by moving temp)
// NOTE: Text pointer internal copy has been updated along the process
strncpy(tempPtr, text, TextLength(text));
memcpy(tempPtr, text, TextLength(text));
}
else TRACELOG(LOG_WARNING, "Text with replacement is longer than internal buffer, use TextReplaceAlloc()");
}
@ -1830,7 +1830,7 @@ char *TextReplace(const char *text, const char *search, const char *replacement)
}
// Replace text string
// REQUIRES: strstr(), strncpy()
// REQUIRES: strstr(), memcpy()
// WARNING: Allocated memory must be manually freed
char *TextReplaceAlloc(const char *text, const char *search, const char *replacement)
{
@ -1887,7 +1887,7 @@ char *TextReplaceAlloc(const char *text, const char *search, const char *replace
// Copy remaind text part after replacement to result (pointed by moving temp)
// NOTE: Text pointer internal copy has been updated along the process
strncpy(temp, text, TextLength(text));
memcpy(temp, text, TextLength(text));
}
}
@ -1975,10 +1975,11 @@ char *TextInsert(const char *text, const char *insert, int position)
{
static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH);
int textLen = TextLength(text);
if ((text != NULL) && (insert != NULL))
if ((text != NULL) && (insert != NULL) && (position >= 0))
{
int textLen = TextLength(text);
if (position > textLen) position = textLen; // End of text string
int insertLen = TextLength(insert);
if ((textLen + insertLen) < (MAX_TEXT_BUFFER_LENGTH - 1))
@ -1987,7 +1988,7 @@ char *TextInsert(const char *text, const char *insert, int position)
for (int i = 0; i < position; i++) buffer[i] = text[i];
for (int i = position; i < insertLen + position; i++) buffer[i] = insert[i - position];
for (int i = (insertLen + position); i < (textLen + insertLen); i++) buffer[i] = text[i];
for (int i = (insertLen + position); i < (textLen + insertLen); i++) buffer[i] = text[i - insertLen];
buffer[textLen + insertLen] = '\0'; // Add EOL
}
@ -2002,17 +2003,18 @@ char *TextInsert(const char *text, const char *insert, int position)
char *TextInsertAlloc(const char *text, const char *insert, int position)
{
char *result = NULL;
int textLen = TextLength(text);
if ((text != NULL) && (insert != NULL))
if ((text != NULL) && (insert != NULL) && (position >= 0))
{
int textLen = TextLength(text);
if (position > textLen) position = textLen; // End of text string
int insertLen = TextLength(insert);
result = (char *)RL_MALLOC(textLen + insertLen + 1);
for (int i = 0; i < position; i++) result[i] = text[i];
for (int i = position; i < insertLen + position; i++) result[i] = insert[i - position];
for (int i = (insertLen + position); i < (textLen + insertLen); i++) result[i] = text[i];
for (int i = position; i < (insertLen + position); i++) result[i] = insert[i - position];
for (int i = (insertLen + position); i < (textLen + insertLen + position); i++) result[i] = text[i - insertLen];
result[textLen + insertLen] = '\0'; // Add EOL
}
@ -2036,7 +2038,7 @@ char *TextJoin(char **textList, int count, const char *delimiter)
int textLength = TextLength(textList[i]);
// Make sure joined text could fit inside MAX_TEXT_BUFFER_LENGTH
if ((totalLength + textLength) < MAX_TEXT_BUFFER_LENGTH)
if ((totalLength + textLength + delimiterLen) < MAX_TEXT_BUFFER_LENGTH)
{
memcpy(textPtr, textList[i], textLength);
totalLength += textLength;

View File

@ -70,6 +70,7 @@
#include <string.h> // Required for: strlen() [Used in ImageTextEx()], strcmp() [Used in LoadImageFromMemory()/LoadImageAnimFromMemory()/ExportImageToMemory()]
#include <math.h> // Required for: fabsf() [Used in DrawTextureRec()]
#include <stdio.h> // Required for: sprintf() [Used in ExportImageAsCode()]
#include <limits.h> // Required for: INT_MAX
// Support only desired texture formats on stb_image
#if !SUPPORT_FILEFORMAT_BMP
@ -813,7 +814,7 @@ bool ExportImageAsCode(Image image, const char *fileName)
// Get file name from path and convert variable name to uppercase
char varFileName[256] = { 0 };
strncpy(varFileName, GetFileNameWithoutExt(fileName), 256 - 1); // NOTE: Using function provided by [rcore] module
snprintf(varFileName, 256, "%s", GetFileNameWithoutExt(fileName)); // 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; }
// Add image information
@ -843,9 +844,10 @@ bool ExportImageAsCode(Image image, const char *fileName)
// Generate image: plain color
Image GenImageColor(int width, int height, Color color)
{
Color *pixels = (Color *)RL_CALLOC(width*height, sizeof(Color));
int dataSize = GetPixelDataSize(width, height, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8);
Color *pixels = (Color *)RL_CALLOC(dataSize, 1);
for (int i = 0; i < width*height; i++) pixels[i] = color;
for (int i = 0; (pixels != NULL) && (i < width*height); i++) pixels[i] = color;
Image image = {
.data = pixels,
@ -1228,18 +1230,25 @@ Image ImageFromImage(Image image, Rectangle rec)
{
Image result = { 0 };
int bytesPerPixel = GetPixelDataSize(1, 1, image.format);
result.width = (int)rec.width;
result.height = (int)rec.height;
result.data = RL_CALLOC((int)rec.width*(int)rec.height*bytesPerPixel, 1);
result.format = image.format;
result.mipmaps = 1;
for (int y = 0; y < (int)rec.height; y++)
// Basic rectangle validation: size smaller than image size
if ((rec.x > 0) && (rec.y > 0) && (rec.width > 0) && (rec.height > 0) &&
(((int)rec.x + (int)rec.width) < image.width) &&
(((int)rec.y + (int)rec.height) < image.height))
{
memcpy(((unsigned char *)result.data) + y*(int)rec.width*bytesPerPixel, ((unsigned char *)image.data) + ((y + (int)rec.y)*image.width + (int)rec.x)*bytesPerPixel, (int)rec.width*bytesPerPixel);
int bytesPerPixel = GetPixelDataSize(1, 1, image.format);
result.width = (int)rec.width;
result.height = (int)rec.height;
result.data = RL_CALLOC((int)rec.width*(int)rec.height*bytesPerPixel, 1);
result.format = image.format;
result.mipmaps = 1;
for (int y = 0; y < (int)rec.height; y++)
{
memcpy(((unsigned char *)result.data) + y*(int)rec.width*bytesPerPixel, ((unsigned char *)image.data) + ((y + (int)rec.y)*image.width + (int)rec.x)*bytesPerPixel, (int)rec.width*bytesPerPixel);
}
}
else TRACELOG(LOG_WARNING, "IMAGE: Rectangle provided for ImageToImage not valid");
return result;
}
@ -2684,45 +2693,48 @@ void ImageRotate(Image *image, int degrees)
int width = (int)(fabsf(image->width*cosRadius) + fabsf(image->height*sinRadius));
int height = (int)(fabsf(image->height*cosRadius) + fabsf(image->width*sinRadius));
int bytesPerPixel = GetPixelDataSize(1, 1, image->format);
unsigned char *rotatedData = (unsigned char *)RL_CALLOC(width*height, bytesPerPixel);
int bytesPerPixel = GetPixelDataSize(width, height, image->format);
unsigned char *rotatedData = (unsigned char *)RL_CALLOC(bytesPerPixel, 1);
for (int y = 0; y < height; y++)
if (rotatedData != NULL)
{
for (int x = 0; x < width; x++)
for (int y = 0; y < height; y++)
{
float oldX = ((x - width/2.0f)*cosRadius + (y - height/2.0f)*sinRadius) + image->width/2.0f;
float oldY = ((y - height/2.0f)*cosRadius - (x - width/2.0f)*sinRadius) + image->height/2.0f;
if ((oldX >= 0) && (oldX < image->width) && (oldY >= 0) && (oldY < image->height))
for (int x = 0; x < width; x++)
{
int x1 = (int)floorf(oldX);
int y1 = (int)floorf(oldY);
int x2 = MIN(x1 + 1, image->width - 1);
int y2 = MIN(y1 + 1, image->height - 1);
float oldX = ((x - width/2.0f)*cosRadius + (y - height/2.0f)*sinRadius) + image->width/2.0f;
float oldY = ((y - height/2.0f)*cosRadius - (x - width/2.0f)*sinRadius) + image->height/2.0f;
float px = oldX - x1;
float py = oldY - y1;
for (int i = 0; i < bytesPerPixel; i++)
if ((oldX >= 0) && (oldX < image->width) && (oldY >= 0) && (oldY < image->height))
{
float f1 = ((unsigned char *)image->data)[(y1*image->width + x1)*bytesPerPixel + i];
float f2 = ((unsigned char *)image->data)[(y1*image->width + x2)*bytesPerPixel + i];
float f3 = ((unsigned char *)image->data)[(y2*image->width + x1)*bytesPerPixel + i];
float f4 = ((unsigned char *)image->data)[(y2*image->width + x2)*bytesPerPixel + i];
int x1 = (int)floorf(oldX);
int y1 = (int)floorf(oldY);
int x2 = MIN(x1 + 1, image->width - 1);
int y2 = MIN(y1 + 1, image->height - 1);
float val = f1*(1 - px)*(1 - py) + f2*px*(1 - py) + f3*(1 - px)*py + f4*px*py;
float px = oldX - x1;
float py = oldY - y1;
rotatedData[(y*width + x)*bytesPerPixel + i] = (unsigned char)val;
for (int i = 0; i < bytesPerPixel; i++)
{
float f1 = ((unsigned char *)image->data)[(y1*image->width + x1)*bytesPerPixel + i];
float f2 = ((unsigned char *)image->data)[(y1*image->width + x2)*bytesPerPixel + i];
float f3 = ((unsigned char *)image->data)[(y2*image->width + x1)*bytesPerPixel + i];
float f4 = ((unsigned char *)image->data)[(y2*image->width + x2)*bytesPerPixel + i];
float val = f1*(1 - px)*(1 - py) + f2*px*(1 - py) + f3*(1 - px)*py + f4*px*py;
rotatedData[(y*width + x)*bytesPerPixel + i] = (unsigned char)val;
}
}
}
}
}
RL_FREE(image->data);
image->data = rotatedData;
image->width = width;
image->height = height;
RL_FREE(image->data);
image->data = rotatedData;
image->width = width;
image->height = height;
}
}
}
@ -3927,7 +3939,7 @@ void ImageDrawRectanglePro(Image *dst, Rectangle rec, Vector2 origin, float rota
// Draw rectangle lines within an image
void ImageDrawRectangleLines(Image *dst, int posX, int posY, int width, int height, Color color)
{
Rectangle rec = { posX, posY, width, height };
Rectangle rec = { (float)posX, (float)posY, (float)width, (float)height };
ImageDrawRectangleLinesEx(dst, rec, 1, color);
}
@ -4019,8 +4031,8 @@ void ImageDrawCircleGradient(Image *dst, Vector2 center, float radius, Color inn
// Draw an image within an image
void ImageDrawImage(Image *dst, Image src, int posX, int posY, Color tint)
{
Rectangle srcRec = { 0, 0, src.width, src.height };
Rectangle dstRec = { posX, posY, srcRec.width, srcRec.height };
Rectangle srcRec = { 0.0f, 0.0f, (float)src.width, (float)src.height };
Rectangle dstRec = { (float)posX, (float)posY, srcRec.width, srcRec.height };
ImageDrawImagePro(dst, src, srcRec, dstRec, (Vector2){ 0 }, 0.0f, tint);
}
@ -5501,17 +5513,24 @@ int GetPixelDataSize(int width, int height, int format)
case PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA: bpp = 2; break;
default: break;
}
unsigned long long dataSizeBytes = (width*height*bpp) >> 3; // Get size in bytes (dividing by 8)
double bytesPerPixel = (double)bpp/8.0;
dataSize = (int)(bytesPerPixel*width*height); // Total data size in bytes
// Most compressed formats works on 4x4 blocks,
// if texture is smaller, minimum dataSize is 8 or 16
if ((width < 4) && (height < 4))
if (dataSizeBytes < INT_MAX)
{
if ((format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) && (format < PIXELFORMAT_COMPRESSED_DXT3_RGBA)) dataSize = 8;
else if ((format >= PIXELFORMAT_COMPRESSED_DXT3_RGBA) && (format < PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA)) dataSize = 16;
dataSize = (int)dataSizeBytes;
// Most compressed formats works on 4x4 blocks,
// if texture is smaller, minimum dataSize is 8 or 16
if ((width < 4) && (height < 4))
{
if ((format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) && (format < PIXELFORMAT_COMPRESSED_DXT3_RGBA)) dataSize = 8;
else if ((format >= PIXELFORMAT_COMPRESSED_DXT3_RGBA) && (format < PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA)) dataSize = 16;
}
}
// NOTE: In case required image data larger than 2GB, no memory allocated at all (NULL)
if (dataSize == 0) TRACELOG(LOG_WARNING, "Requested image size is larger than 2GB, it can not be allocated");
return dataSize;
}

View File

@ -2331,7 +2331,7 @@ static rlExampleInfo *LoadExampleInfo(const char *exFileName)
// Example found in collection
exInfo = (rlExampleInfo *)RL_CALLOC(1, sizeof(rlExampleInfo));
strncpy(exInfo->name, GetFileNameWithoutExt(exFileName), 128 - 1);
snprintf(exInfo->name, 128, "%s", GetFileNameWithoutExt(exFileName));
strncpy(exInfo->category, exInfo->name, TextFindIndex(exInfo->name, "_"));
char *exText = LoadFileText(exFileName);
@ -2377,7 +2377,7 @@ static rlExampleInfo *LoadExampleInfo(const char *exFileName)
int copyrightIndex = TextFindIndex(exText, "Copyright (c) ");
int yearStartIndex = copyrightIndex + 14;
char yearText[5] = { 0 };
strncpy(yearText, exText + yearStartIndex, 4);
snprintf(yearText, 5, "%s", exText + yearStartIndex);
exInfo->yearCreated = TextToInteger(yearText);
// Check for review year included (or just use creation year)
if (exText[yearStartIndex + 4] == '-') strncpy(yearText, exText + yearStartIndex + 5, 4);
@ -2425,7 +2425,7 @@ static int ParseExampleInfoLine(const char *line, rlExampleInfo *entry)
#define MAX_EXAMPLE_INFO_LINE_LEN 512
char temp[MAX_EXAMPLE_INFO_LINE_LEN] = { 0 };
strncpy(temp, line, MAX_EXAMPLE_INFO_LINE_LEN);
snprintf(temp, MAX_EXAMPLE_INFO_LINE_LEN, "%s", line);
temp[MAX_EXAMPLE_INFO_LINE_LEN - 1] = '\0'; // Ensure null termination
int tokenCount = 0;
@ -2902,7 +2902,7 @@ static void UpdateWebMetadata(const char *exHtmlPath, const char *exFilePath)
char exTitle[64] = { 0 }; // Example title: fileName without extension, replacing underscores by spaces
// Get example name: replace underscore by spaces
strncpy(exName, GetFileNameWithoutExt(exHtmlPathCopy), 64 - 1);
snprintf(exName, 64, "%s", GetFileNameWithoutExt(exHtmlPathCopy));
strcpy(exTitle, exName);
for (int i = 0; (i < 64) && (exTitle[i] != '\0'); i++) { if (exTitle[i] == '_') exTitle[i] = ' '; }

View File

@ -968,7 +968,7 @@
"description": "Skeleton, animation bones hierarchy",
"fields": [
{
"type": "int",
"type": "unsigned int",
"name": "boneCount",
"description": "Number of bones"
},
@ -1045,7 +1045,7 @@
"description": "Animation name"
},
{
"type": "int",
"type": "unsigned int",
"name": "boneCount",
"description": "Number of bones (per pose)"
},

View File

@ -968,7 +968,7 @@ return {
description = "Skeleton, animation bones hierarchy",
fields = {
{
type = "int",
type = "unsigned int",
name = "boneCount",
description = "Number of bones"
},
@ -1045,7 +1045,7 @@ return {
description = "Animation name"
},
{
type = "int",
type = "unsigned int",
name = "boneCount",
description = "Number of bones (per pose)"
},

View File

@ -611,7 +611,7 @@
((name "ModelSkeleton")
(description "Skeleton, animation bones hierarchy")
(fields
((type "int")
((type "unsigned int")
(name "boneCount")
(description "Number of bones"))
((type "BoneInfo *")
@ -658,7 +658,7 @@
((type "char[32]")
(name "name")
(description "Animation name"))
((type "int")
((type "unsigned int")
(name "boneCount")
(description "Number of bones (per pose)"))
((type "int")

View File

@ -448,7 +448,7 @@ Struct 20: BoneInfo (2 fields)
Struct 21: ModelSkeleton (3 fields)
Name: ModelSkeleton
Description: Skeleton, animation bones hierarchy
Field[1]: int boneCount // Number of bones
Field[1]: unsigned int boneCount // Number of bones
Field[2]: BoneInfo * bones // Bones information (skeleton)
Field[3]: ModelAnimPose bindPose // Bones base transformation (Transform[])
Struct 22: Model (9 fields)
@ -467,7 +467,7 @@ Struct 23: ModelAnimation (4 fields)
Name: ModelAnimation
Description: ModelAnimation, contains a full animation sequence
Field[1]: char[32] name // Animation name
Field[2]: int boneCount // Number of bones (per pose)
Field[2]: unsigned int boneCount // Number of bones (per pose)
Field[3]: int keyframeCount // Number of animation key frames
Field[4]: ModelAnimPose * keyframePoses // Animation sequence keyframe poses [keyframe][pose]
Struct 24: Ray (2 fields)

View File

@ -201,7 +201,7 @@
<Field type="int" name="parent" desc="Bone parent" />
</Struct>
<Struct name="ModelSkeleton" fieldCount="3" desc="Skeleton, animation bones hierarchy">
<Field type="int" name="boneCount" desc="Number of bones" />
<Field type="unsigned int" name="boneCount" desc="Number of bones" />
<Field type="BoneInfo *" name="bones" desc="Bones information (skeleton)" />
<Field type="ModelAnimPose" name="bindPose" desc="Bones base transformation (Transform[])" />
</Struct>
@ -218,7 +218,7 @@
</Struct>
<Struct name="ModelAnimation" fieldCount="4" desc="ModelAnimation, contains a full animation sequence">
<Field type="char[32]" name="name" desc="Animation name" />
<Field type="int" name="boneCount" desc="Number of bones (per pose)" />
<Field type="unsigned int" name="boneCount" desc="Number of bones (per pose)" />
<Field type="int" name="keyframeCount" desc="Number of animation key frames" />
<Field type="ModelAnimPose *" name="keyframePoses" desc="Animation sequence keyframe poses [keyframe][pose]" />
</Struct>