From 352db3a1830e7db3f0484db907f524f4f144b9af Mon Sep 17 00:00:00 2001 From: Bxil Date: Fri, 21 May 2021 21:59:21 +0200 Subject: [PATCH 01/38] Removes debug information from release builds (#1780) --- projects/VS2017/raylib/raylib.vcxproj | 2 ++ projects/VS2019/raylib/raylib.vcxproj | 2 ++ 2 files changed, 4 insertions(+) diff --git a/projects/VS2017/raylib/raylib.vcxproj b/projects/VS2017/raylib/raylib.vcxproj index 11ecb4048..2567b0ccf 100644 --- a/projects/VS2017/raylib/raylib.vcxproj +++ b/projects/VS2017/raylib/raylib.vcxproj @@ -252,6 +252,7 @@ _CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions);GRAPHICS_API_OPENGL_33;PLATFORM_DESKTOP $(ProjectDir)..\..\..\src\external\glfw\include CompileAsC + Windows @@ -291,6 +292,7 @@ $(ProjectDir)..\..\..\src\external\glfw\include CompileAsC MultiThreaded + Windows diff --git a/projects/VS2019/raylib/raylib.vcxproj b/projects/VS2019/raylib/raylib.vcxproj index 0ce079fd1..f95daaf38 100644 --- a/projects/VS2019/raylib/raylib.vcxproj +++ b/projects/VS2019/raylib/raylib.vcxproj @@ -252,6 +252,7 @@ _CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions);GRAPHICS_API_OPENGL_33;PLATFORM_DESKTOP $(ProjectDir)..\..\..\src\external\glfw\include CompileAsC + Windows @@ -291,6 +292,7 @@ $(ProjectDir)..\..\..\src\external\glfw\include CompileAsC MultiThreaded + Windows From 60ca8acd0ad6ffc2022f8736ab80606f498a2e6e Mon Sep 17 00:00:00 2001 From: raysan5 Date: Fri, 21 May 2021 22:59:00 +0200 Subject: [PATCH 02/38] Update version to 3.8-dev to note breaking changes Some breaking changes were introduced lately, it's better to note that current version is not 3.7 anymore... --- src/Makefile | 4 ++-- src/raylib.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Makefile b/src/Makefile index a4a943d6c..9e4d6b0d2 100644 --- a/src/Makefile +++ b/src/Makefile @@ -43,8 +43,8 @@ .PHONY: all clean install uninstall # Define required raylib variables -RAYLIB_VERSION = 3.7.0 -RAYLIB_API_VERSION = 370 +RAYLIB_VERSION = 3.8-dev +RAYLIB_API_VERSION = 381 # Define raylib source code path RAYLIB_SRC_PATH ?= ../src diff --git a/src/raylib.h b/src/raylib.h index b0b6b9f66..767891fa1 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -81,7 +81,7 @@ #include // Required for: va_list - Only used by TraceLogCallback -#define RAYLIB_VERSION "3.7.0" +#define RAYLIB_VERSION "3.8-dev" #if defined(_WIN32) // Microsoft attibutes to tell compiler that symbols are imported/exported from a .dll From 5765b7895cfe3faba447748a6d63564da4077ec1 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 22 May 2021 14:35:10 +0200 Subject: [PATCH 03/38] Minor comments tweaks --- src/raylib.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 767891fa1..8f3612cf3 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -970,11 +970,11 @@ RLAPI bool IsCursorOnScreen(void); // Check if cu RLAPI void ClearBackground(Color color); // Set background color (framebuffer clear color) RLAPI void BeginDrawing(void); // Setup canvas (framebuffer) to start drawing RLAPI void EndDrawing(void); // End canvas drawing and swap buffers (double buffering) -RLAPI void BeginMode2D(Camera2D camera); // Initialize 2D mode with custom camera (2D) +RLAPI void BeginMode2D(Camera2D camera); // Begin 2D mode with custom camera (2D) RLAPI void EndMode2D(void); // Ends 2D mode with custom camera -RLAPI void BeginMode3D(Camera3D camera); // Initializes 3D mode with custom camera (3D) +RLAPI void BeginMode3D(Camera3D camera); // Begin 3D mode with custom camera (3D) RLAPI void EndMode3D(void); // Ends 3D mode and returns to default 2D orthographic mode -RLAPI void BeginTextureMode(RenderTexture2D target); // Initializes render texture for drawing +RLAPI void BeginTextureMode(RenderTexture2D target); // Begin drawing to render texture RLAPI void EndTextureMode(void); // Ends drawing to render texture RLAPI void BeginShaderMode(Shader shader); // Begin custom shader drawing RLAPI void EndShaderMode(void); // End custom shader drawing (use default shader) @@ -1143,7 +1143,7 @@ RLAPI void SetCameraMoveControls(int keyFront, int keyBack, int keyRight, int ke // Set texture and rectangle to be used on shapes drawing // NOTE: It can be useful when using basic shapes and one single font, // defining a font char white rectangle would allow drawing everything in a single draw call -RLAPI void SetShapesTexture(Texture2D texture, Rectangle source); +RLAPI void SetShapesTexture(Texture2D texture, Rectangle source); // Set texture and rectangle to be used on shapes drawing // Basic shapes drawing functions RLAPI void DrawPixel(int posX, int posY, Color color); // Draw a pixel @@ -1232,7 +1232,7 @@ RLAPI void ImageAlphaPremultiply(Image *image); RLAPI void ImageResize(Image *image, int newWidth, int newHeight); // Resize image (Bicubic scaling algorithm) RLAPI void ImageResizeNN(Image *image, int newWidth,int newHeight); // Resize image (Nearest-Neighbor scaling algorithm) RLAPI void ImageResizeCanvas(Image *image, int newWidth, int newHeight, int offsetX, int offsetY, Color fill); // Resize canvas and fill with color -RLAPI void ImageMipmaps(Image *image); // Generate all mipmap levels for a provided image +RLAPI void ImageMipmaps(Image *image); // Compute all mipmap levels for a provided image RLAPI void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp); // Dither image data to 16bpp or lower (Floyd-Steinberg dithering) RLAPI void ImageFlipVertical(Image *image); // Flip image vertically RLAPI void ImageFlipHorizontal(Image *image); // Flip image horizontally @@ -1433,7 +1433,7 @@ RLAPI Mesh GenMeshHeightmap(Image heightmap, Vector3 size); RLAPI Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize); // Generate cubes-based map mesh from image data // Mesh manipulation functions -RLAPI BoundingBox GetMeshBoundingBox(Mesh mesh); // Compute mesh bounding box limits +RLAPI BoundingBox GetMeshBoundingBox(Mesh mesh); // Compute mesh bounding box limits RLAPI void MeshTangents(Mesh *mesh); // Compute mesh tangents RLAPI void MeshBinormals(Mesh *mesh); // Compute mesh binormals From b36422083ee883a40407c3cb86518aca9988f4ec Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 22 May 2021 16:09:34 +0200 Subject: [PATCH 04/38] Remove headers from shaders --- examples/models/resources/shaders/glsl330/brdf.fs | 13 ++----------- examples/models/resources/shaders/glsl330/brdf.vs | 8 -------- .../models/resources/shaders/glsl330/cubemap.vs | 8 -------- .../models/resources/shaders/glsl330/irradiance.fs | 8 -------- examples/models/resources/shaders/glsl330/pbr.fs | 8 -------- examples/models/resources/shaders/glsl330/pbr.vs | 8 -------- .../models/resources/shaders/glsl330/prefilter.fs | 13 +++---------- examples/models/resources/shaders/glsl330/skybox.fs | 10 ---------- examples/models/resources/shaders/glsl330/skybox.vs | 8 -------- 9 files changed, 5 insertions(+), 79 deletions(-) diff --git a/examples/models/resources/shaders/glsl330/brdf.fs b/examples/models/resources/shaders/glsl330/brdf.fs index f2774f81c..5f44bfe5f 100644 --- a/examples/models/resources/shaders/glsl330/brdf.fs +++ b/examples/models/resources/shaders/glsl330/brdf.fs @@ -1,16 +1,5 @@ -/******************************************************************************************* -* -* BRDF LUT Generation - Bidirectional reflectance distribution function fragment shader -* -* REF: https://github.com/HectorMF/BRDFGenerator -* -* Copyright (c) 2017 Victor Fisac -* -**********************************************************************************************/ - #version 330 - // Input vertex attributes (from vertex shader) in vec2 fragTexCoord; @@ -86,6 +75,8 @@ float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) return ggx1*ggx2; } +// Bidirectional reflectance distribution function +// Ref: https://github.com/HectorMF/BRDFGenerator vec2 IntegrateBRDF(float NdotV, float roughness) { float A = 0.0; diff --git a/examples/models/resources/shaders/glsl330/brdf.vs b/examples/models/resources/shaders/glsl330/brdf.vs index 06384673b..c93cfd0ea 100644 --- a/examples/models/resources/shaders/glsl330/brdf.vs +++ b/examples/models/resources/shaders/glsl330/brdf.vs @@ -1,11 +1,3 @@ -/******************************************************************************************* -* -* rPBR [shader] - Bidirectional reflectance distribution function vertex shader -* -* Copyright (c) 2017 Victor Fisac -* -**********************************************************************************************/ - #version 330 // Input vertex attributes diff --git a/examples/models/resources/shaders/glsl330/cubemap.vs b/examples/models/resources/shaders/glsl330/cubemap.vs index 5721eaa2c..d3565dfdc 100644 --- a/examples/models/resources/shaders/glsl330/cubemap.vs +++ b/examples/models/resources/shaders/glsl330/cubemap.vs @@ -1,11 +1,3 @@ -/******************************************************************************************* -* -* rPBR [shader] - Equirectangular to cubemap vertex shader -* -* Copyright (c) 2017 Victor Fisac -* -**********************************************************************************************/ - #version 330 // Input vertex attributes diff --git a/examples/models/resources/shaders/glsl330/irradiance.fs b/examples/models/resources/shaders/glsl330/irradiance.fs index 4ef03e052..4b74916d9 100644 --- a/examples/models/resources/shaders/glsl330/irradiance.fs +++ b/examples/models/resources/shaders/glsl330/irradiance.fs @@ -1,11 +1,3 @@ -/******************************************************************************************* -* -* rPBR [shader] - Irradiance cubemap fragment shader -* -* Copyright (c) 2017 Victor Fisac -* -**********************************************************************************************/ - #version 330 // Input vertex attributes (from vertex shader) diff --git a/examples/models/resources/shaders/glsl330/pbr.fs b/examples/models/resources/shaders/glsl330/pbr.fs index 68bf203a2..72779a185 100644 --- a/examples/models/resources/shaders/glsl330/pbr.fs +++ b/examples/models/resources/shaders/glsl330/pbr.fs @@ -1,11 +1,3 @@ -/******************************************************************************************* -* -* rPBR [shader] - Physically based rendering fragment shader -* -* Copyright (c) 2017 Victor Fisac -* -**********************************************************************************************/ - #version 330 #define MAX_REFLECTION_LOD 4.0 diff --git a/examples/models/resources/shaders/glsl330/pbr.vs b/examples/models/resources/shaders/glsl330/pbr.vs index 045428c68..4729b0141 100644 --- a/examples/models/resources/shaders/glsl330/pbr.vs +++ b/examples/models/resources/shaders/glsl330/pbr.vs @@ -1,11 +1,3 @@ -/******************************************************************************************* -* -* rPBR [shader] - Physically based rendering vertex shader -* -* Copyright (c) 2017 Victor Fisac -* -**********************************************************************************************/ - #version 330 // Input vertex attributes diff --git a/examples/models/resources/shaders/glsl330/prefilter.fs b/examples/models/resources/shaders/glsl330/prefilter.fs index 82f0f7710..3ea694f95 100644 --- a/examples/models/resources/shaders/glsl330/prefilter.fs +++ b/examples/models/resources/shaders/glsl330/prefilter.fs @@ -1,14 +1,7 @@ -/******************************************************************************************* -* -* rPBR [shader] - Prefiltered environment for reflections fragment shader -* -* Copyright (c) 2017 Victor Fisac -* -**********************************************************************************************/ - #version 330 -#define MAX_SAMPLES 1024u -#define CUBEMAP_RESOLUTION 1024.0 + +#define MAX_SAMPLES 1024u +#define CUBEMAP_RESOLUTION 1024.0 // Input vertex attributes (from vertex shader) in vec3 fragPosition; diff --git a/examples/models/resources/shaders/glsl330/skybox.fs b/examples/models/resources/shaders/glsl330/skybox.fs index 800c1c6fc..bb96fc3c3 100644 --- a/examples/models/resources/shaders/glsl330/skybox.fs +++ b/examples/models/resources/shaders/glsl330/skybox.fs @@ -1,13 +1,3 @@ -/******************************************************************************************* -* -* rPBR [shader] - Background skybox fragment shader -* -* Copyright (c) 2017 Victor Fisac -* -* 19-Jun-2020 - modified by Giuseppe Mastrangelo (@peppemas) - VFlip Support -* -**********************************************************************************************/ - #version 330 // Input vertex attributes (from vertex shader) diff --git a/examples/models/resources/shaders/glsl330/skybox.vs b/examples/models/resources/shaders/glsl330/skybox.vs index 4fe9a2c30..6279bc497 100644 --- a/examples/models/resources/shaders/glsl330/skybox.vs +++ b/examples/models/resources/shaders/glsl330/skybox.vs @@ -1,11 +1,3 @@ -/******************************************************************************************* -* -* rPBR [shader] - Background skybox vertex shader -* -* Copyright (c) 2017 Victor Fisac -* -**********************************************************************************************/ - #version 330 // Input vertex attributes From cb698dd37db805a7d33445c22d6077fc35cf288d Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 22 May 2021 16:51:51 +0200 Subject: [PATCH 05/38] Update Makefile --- src/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Makefile b/src/Makefile index 9e4d6b0d2..f28a050bc 100644 --- a/src/Makefile +++ b/src/Makefile @@ -43,8 +43,8 @@ .PHONY: all clean install uninstall # Define required raylib variables -RAYLIB_VERSION = 3.8-dev -RAYLIB_API_VERSION = 381 +RAYLIB_VERSION = 3.7.1 +RAYLIB_API_VERSION = 371 # Define raylib source code path RAYLIB_SRC_PATH ?= ../src From 03710c9d8ea5acb04445ce3d125b58da043df6ed Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 22 May 2021 16:54:04 +0200 Subject: [PATCH 06/38] Some code tweaks for consistency --- src/core.c | 12 ++++++------ src/models.c | 22 +++++++++++----------- src/raylib.h | 6 +++--- src/rlgl.h | 4 ++-- src/text.c | 2 +- src/utils.c | 2 +- 6 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/core.c b/src/core.c index c59245950..e84d82c49 100644 --- a/src/core.c +++ b/src/core.c @@ -2179,8 +2179,8 @@ Shader LoadShader(const char *vsFileName, const char *fsFileName) shader.id = rlLoadShaderCode(vShaderStr, fShaderStr); - if (vShaderStr != NULL) RL_FREE(vShaderStr); - if (fShaderStr != NULL) RL_FREE(fShaderStr); + if (vShaderStr != NULL) UnloadFileText(vShaderStr); + if (fShaderStr != NULL) UnloadFileText(fShaderStr); // After shader loading, we TRY to set default location names if (shader.id > 0) @@ -2801,8 +2801,8 @@ char **GetDirectoryFiles(const char *dirPath, int *fileCount) ClearDirectoryFiles(); // Memory allocation for MAX_DIRECTORY_FILES - dirFilesPath = (char **)RL_MALLOC(sizeof(char *)*MAX_DIRECTORY_FILES); - for (int i = 0; i < MAX_DIRECTORY_FILES; i++) dirFilesPath[i] = (char *)RL_MALLOC(sizeof(char)*MAX_FILEPATH_LENGTH); + dirFilesPath = (char **)RL_MALLOC(MAX_DIRECTORY_FILES*sizeof(char *)); + for (int i = 0; i < MAX_DIRECTORY_FILES; i++) dirFilesPath[i] = (char *)RL_MALLOC(MAX_FILEPATH_LENGTH*sizeof(char)); int counter = 0; struct dirent *entity; @@ -5151,11 +5151,11 @@ static void WindowDropCallback(GLFWwindow *window, int count, const char **paths { ClearDroppedFiles(); - CORE.Window.dropFilesPath = (char **)RL_MALLOC(sizeof(char *)*count); + CORE.Window.dropFilesPath = (char **)RL_MALLOC(count*sizeof(char *)); for (int i = 0; i < count; i++) { - CORE.Window.dropFilesPath[i] = (char *)RL_MALLOC(sizeof(char)*MAX_FILEPATH_LENGTH); + CORE.Window.dropFilesPath[i] = (char *)RL_MALLOC(MAX_FILEPATH_LENGTH*sizeof(char)); strcpy(CORE.Window.dropFilesPath[i], paths[i]); } diff --git a/src/models.c b/src/models.c index b9d24e101..551ee7af2 100644 --- a/src/models.c +++ b/src/models.c @@ -3205,11 +3205,11 @@ static Model LoadOBJ(const char *fileName) tinyobj_material_t *materials = NULL; unsigned int materialCount = 0; - char *fileData = LoadFileText(fileName); + char *fileText = LoadFileText(fileName); - if (fileData != NULL) + if (fileText != NULL) { - unsigned int dataSize = (unsigned int)strlen(fileData); + unsigned int dataSize = (unsigned int)strlen(fileText); char currentDir[1024] = { 0 }; strcpy(currentDir, GetWorkingDirectory()); const char *workingDir = GetDirectoryPath(fileName); @@ -3219,7 +3219,7 @@ static Model LoadOBJ(const char *fileName) } unsigned int flags = TINYOBJ_FLAG_TRIANGULATE; - int ret = tinyobj_parse_obj(&attrib, &meshes, &meshCount, &materials, &materialCount, fileData, dataSize, flags); + int ret = tinyobj_parse_obj(&attrib, &meshes, &meshCount, &materials, &materialCount, fileText, dataSize, flags); if (ret != TINYOBJ_SUCCESS) TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load OBJ data", fileName); else TRACELOG(LOG_INFO, "MODEL: [%s] OBJ data loaded successfully: %i meshes / %i materials", fileName, meshCount, materialCount); @@ -3346,9 +3346,9 @@ static Model LoadOBJ(const char *fileName) tinyobj_shapes_free(meshes, meshCount); tinyobj_materials_free(materials, materialCount); - RL_FREE(fileData); + UnloadFileText(fileText); + RL_FREE(matFaces); - RL_FREE(vCount); RL_FREE(vtCount); RL_FREE(vnCount); @@ -3496,7 +3496,7 @@ static Model LoadIQM(const char *fileName) //fileDataPtr += sizeof(IQMHeader); // Move file data pointer // Meshes data processing - imesh = RL_MALLOC(sizeof(IQMMesh)*iqmHeader->num_meshes); + imesh = RL_MALLOC(iqmHeader->num_meshes*sizeof(IQMMesh)); //fseek(iqmFile, iqmHeader->ofs_meshes, SEEK_SET); //fread(imesh, sizeof(IQMMesh)*iqmHeader->num_meshes, 1, iqmFile); memcpy(imesh, fileDataPtr + iqmHeader->ofs_meshes, iqmHeader->num_meshes*sizeof(IQMMesh)); @@ -4466,8 +4466,8 @@ static void LoadGLTFBoneAttribute(Model* model, cgltf_accessor* jointsAccessor, { if (jointsAccessor->component_type == cgltf_component_type_r_16u) { - model->meshes[primitiveIndex].boneIds = RL_MALLOC(sizeof(int)*jointsAccessor->count*4); - short* bones = RL_MALLOC(sizeof(short)*jointsAccessor->count*4); + model->meshes[primitiveIndex].boneIds = RL_MALLOC(jointsAccessor->count*4*sizeof(int)); + short* bones = RL_MALLOC(jointsAccessor->count*4*sizeof(short)); for (unsigned int a = 0; a < jointsAccessor->count; a++) { @@ -4491,8 +4491,8 @@ static void LoadGLTFBoneAttribute(Model* model, cgltf_accessor* jointsAccessor, } else if (jointsAccessor->component_type == cgltf_component_type_r_8u) { - model->meshes[primitiveIndex].boneIds = RL_MALLOC(sizeof(int)*jointsAccessor->count*4); - unsigned char* bones = RL_MALLOC(sizeof(unsigned char)*jointsAccessor->count*4); + model->meshes[primitiveIndex].boneIds = RL_MALLOC(jointsAccessor->count*4*sizeof(int)); + unsigned char* bones = RL_MALLOC(jointsAccessor->count*4*sizeof(unsigned char)); for (unsigned int a = 0; a < jointsAccessor->count; a++) { diff --git a/src/raylib.h b/src/raylib.h index 8f3612cf3..b468961b1 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -898,9 +898,9 @@ typedef enum { // Callbacks to hook some internal functions // WARNING: This callbacks are intended for advance users typedef void (*TraceLogCallback)(int logLevel, const char *text, va_list args); // Logging: Redirect trace log messages -typedef unsigned char* (*LoadFileDataCallback)(const char* fileName, unsigned int* bytesRead); // FileIO: Load binary data +typedef unsigned char *(*LoadFileDataCallback)(const char *fileName, unsigned int *bytesRead); // FileIO: Load binary data typedef bool (*SaveFileDataCallback)(const char *fileName, void *data, unsigned int bytesToWrite); // FileIO: Save binary data -typedef char *(*LoadFileTextCallback)(const char* fileName); // FileIO: Load text data +typedef char *(*LoadFileTextCallback)(const char *fileName); // FileIO: Load text data typedef bool (*SaveFileTextCallback)(const char *fileName, char *text); // FileIO: Save text data @@ -1040,7 +1040,7 @@ RLAPI unsigned char *LoadFileData(const char *fileName, unsigned int *bytesRead) RLAPI void UnloadFileData(unsigned char *data); // Unload file data allocated by LoadFileData() RLAPI bool SaveFileData(const char *fileName, void *data, unsigned int bytesToWrite); // Save data to file from byte array (write), returns true on success RLAPI char *LoadFileText(const char *fileName); // Load text data from file (read), returns a '\0' terminated string -RLAPI void UnloadFileText(unsigned char *text); // Unload file text data allocated by LoadFileText() +RLAPI void UnloadFileText(char *text); // Unload file text data allocated by LoadFileText() RLAPI bool SaveFileText(const char *fileName, char *text); // Save text data to file (write), string must be '\0' terminated, returns true on success RLAPI bool FileExists(const char *fileName); // Check if file exists RLAPI bool DirectoryExists(const char *dirPath); // Check if a directory path exists diff --git a/src/rlgl.h b/src/rlgl.h index c60a2a701..a40c80477 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -1685,7 +1685,7 @@ void rlLoadExtensions(void *loader) #if defined(SUPPORT_GL_DETAILS_INFO) // Get supported extensions list // WARNING: glGetStringi() not available on OpenGL 2.1 - char **extList = RL_MALLOC(sizeof(char *)*numExt); + char **extList = RL_MALLOC(numExt*sizeof(char *)); TRACELOG(LOG_INFO, "GL: OpenGL extensions:"); for (int i = 0; i < numExt; i++) { @@ -1966,7 +1966,7 @@ RenderBatch rlLoadRenderBatch(int numBuffers, int bufferElements) #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) // Initialize CPU (RAM) vertex buffers (position, texcoord, color data and indexes) //-------------------------------------------------------------------------------------------- - batch.vertexBuffer = (VertexBuffer *)RL_MALLOC(sizeof(VertexBuffer)*numBuffers); + batch.vertexBuffer = (VertexBuffer *)RL_MALLOC(numBuffers*sizeof(VertexBuffer)); for (int i = 0; i < numBuffers; i++) { diff --git a/src/text.c b/src/text.c index 3e821fe80..23cad7e8d 100644 --- a/src/text.c +++ b/src/text.c @@ -1865,7 +1865,7 @@ static Font LoadBMFont(const char *fileName) } UnloadImage(imFont); - RL_FREE(fileText); + UnloadFileText(fileText); if (font.texture.id == 0) { diff --git a/src/utils.c b/src/utils.c index 66804fb2c..c21d23181 100644 --- a/src/utils.c +++ b/src/utils.c @@ -325,7 +325,7 @@ char *LoadFileText(const char *fileName) } // Unload file text data allocated by LoadFileText() -void UnloadFileText(unsigned char *text) +void UnloadFileText(char *text) { RL_FREE(text); } From 6c874feb7bef71772859ae1804dbd60643f5dbc0 Mon Sep 17 00:00:00 2001 From: Chris Date: Sun, 23 May 2021 16:52:17 +0100 Subject: [PATCH 07/38] Update BINDINGS.md (#1782) --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index bd03c3154..90c0b9283 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -8,7 +8,7 @@ Here it is a list with the ones I'm aware of: |:------------------:|:-------------: | :--------:|----------------------------------------------------------------------| | raylib | **3.7** | [C](https://en.wikipedia.org/wiki/C_(programming_language)) | https://github.com/raysan5/raylib | | raylib-cpp | 3.7 | [C++](https://en.wikipedia.org/wiki/C%2B%2B) | https://github.com/robloach/raylib-cpp | -| Raylib-cs | 3.5 | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | https://github.com/ChrisDill/Raylib-cs | +| Raylib-cs | 3.7 | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | https://github.com/ChrisDill/Raylib-cs | | raylib-cppsharp | 2.5 | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | https://github.com/phxvyper/raylib-cppsharp | | raylib-boo | 3.7 | [Boo](http://boo-language.github.io/) | https://github.com/Rabios/raylib-boo | | RaylibFS | 2.5 | [F#](https://fsharp.org/) | https://github.com/dallinbeutler/RaylibFS | From e5e401c640564381f8353ab9e2338e6f2cb3a498 Mon Sep 17 00:00:00 2001 From: DanielG <42619318+GoldenbergDaniel@users.noreply.github.com> Date: Mon, 24 May 2021 05:05:08 -0400 Subject: [PATCH 08/38] Fixed CMake linking on MacOS (#1783) Added links to IOKit, Cocoa, and OpenGL to the CMake build file for MacOS platforms --- projects/CMake/CMakeLists.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/projects/CMake/CMakeLists.txt b/projects/CMake/CMakeLists.txt index 496577e92..0876f0d55 100644 --- a/projects/CMake/CMakeLists.txt +++ b/projects/CMake/CMakeLists.txt @@ -34,4 +34,11 @@ add_executable(${PROJECT_NAME} core_basic_window.c) #set(raylib_VERBOSE 1) target_link_libraries(${PROJECT_NAME} raylib) +# Checks if OSX and links appropriate frameworks (Only required on MacOS) +if (APPLE) + target_link_libraries(${PROJECT_NAME} "-framework IOKit") + target_link_libraries(${PROJECT_NAME} "-framework Cocoa") + target_link_libraries(${PROJECT_NAME} "-framework OpenGL") +endif() + # That's it! You should have an example executable that you can run. Have fun! From 9f45fea31eb53380baf14566c39eb791f157b7e2 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 25 May 2021 11:59:34 +0200 Subject: [PATCH 09/38] Remove line break in DrawTextRecEx() --- src/raylib.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index b468961b1..0394315fa 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1330,8 +1330,7 @@ RLAPI void DrawFPS(int posX, int posY); RLAPI void DrawText(const char *text, int posX, int posY, int fontSize, Color color); // Draw text (using default font) RLAPI void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint); // Draw text using font and additional parameters RLAPI void DrawTextRec(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint); // Draw text using font inside rectangle limits -RLAPI void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint, - int selectStart, int selectLength, Color selectTint, Color selectBackTint); // Draw text using font inside rectangle limits with support for text selection +RLAPI void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint, int selectStart, int selectLength, Color selectTint, Color selectBackTint); // Draw text using font inside rectangle limits with support for text selection RLAPI void DrawTextCodepoint(Font font, int codepoint, Vector2 position, float fontSize, Color tint); // Draw one character (codepoint) // Text misc. functions From 6fe0a229e2038318f8ccdd1f0c4c64c322f6d0ea Mon Sep 17 00:00:00 2001 From: Ben Beshara Date: Tue, 25 May 2021 20:19:36 +1000 Subject: [PATCH 10/38] Bumped minimum GLFW vers in cmake (#1788) --- cmake/GlfwImport.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/GlfwImport.cmake b/cmake/GlfwImport.cmake index b740884f7..b0ae162d4 100644 --- a/cmake/GlfwImport.cmake +++ b/cmake/GlfwImport.cmake @@ -1,8 +1,8 @@ if(USE_EXTERNAL_GLFW STREQUAL "ON") - find_package(glfw3 3.2.1 REQUIRED) + find_package(glfw3 3.3.3 REQUIRED) elseif(USE_EXTERNAL_GLFW STREQUAL "IF_POSSIBLE") - find_package(glfw3 3.2.1 QUIET) + find_package(glfw3 3.3.3 QUIET) endif() if (glfw3_FOUND) set(LIBS_PRIVATE ${LIBS_PRIVATE} glfw) From 7959d95db0659055519a8665bf2b8e268410d338 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 25 May 2021 23:19:03 +0200 Subject: [PATCH 11/38] Review raylib structs comments --- src/raylib.h | 172 +++++++++++++++++++++++++-------------------------- 1 file changed, 86 insertions(+), 86 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 0394315fa..67fde1574 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -181,23 +181,23 @@ // Vector2 type typedef struct Vector2 { - float x; - float y; + float x; // Vector x component + float y; // Vector y component } Vector2; // Vector3 type typedef struct Vector3 { - float x; - float y; - float z; + float x; // Vector x component + float y; // Vector y component + float z; // Vector z component } Vector3; // Vector4 type typedef struct Vector4 { - float x; - float y; - float z; - float w; + float x; // Vector x component + float y; // Vector y component + float z; // Vector z component + float w; // Vector w component } Vector4; // Quaternion type, same as Vector4 @@ -205,26 +205,26 @@ typedef Vector4 Quaternion; // Matrix type (OpenGL style 4x4 - right handed, column major) typedef struct Matrix { - float m0, m4, m8, m12; - float m1, m5, m9, m13; - float m2, m6, m10, m14; - float m3, m7, m11, m15; + float m0, m4, m8, m12; // Matrix first row (4 components) + float m1, m5, m9, m13; // Matrix second row (4 components) + float m2, m6, m10, m14; // Matrix third row (4 components) + float m3, m7, m11, m15; // Matrix fourth row (4 components) } Matrix; // Color type, RGBA (32bit) typedef struct Color { - unsigned char r; - unsigned char g; - unsigned char b; - unsigned char a; + unsigned char r; // Color red value + unsigned char g; // Color green value + unsigned char b; // Color blue value + unsigned char a; // Color alpha value } Color; // Rectangle type typedef struct Rectangle { - float x; - float y; - float width; - float height; + float x; // Rectangle top-left corner position x + float y; // Rectangle top-left corner position y + float width; // Rectangle width + float height; // Rectangle height } Rectangle; // Image type, bpp always RGBA (32bit) @@ -316,113 +316,113 @@ typedef struct Camera2D { // Vertex data definning a mesh // NOTE: Data stored in CPU memory (and GPU) typedef struct Mesh { - int vertexCount; // Number of vertices stored in arrays - int triangleCount; // Number of triangles stored (indexed or not) + int vertexCount; // Number of vertices stored in arrays + int triangleCount; // Number of triangles stored (indexed or not) // Default vertex data - float *vertices; // Vertex position (XYZ - 3 components per vertex) (shader-location = 0) - float *texcoords; // Vertex texture coordinates (UV - 2 components per vertex) (shader-location = 1) - float *texcoords2; // Vertex second texture coordinates (useful for lightmaps) (shader-location = 5) - float *normals; // Vertex normals (XYZ - 3 components per vertex) (shader-location = 2) - float *tangents; // Vertex tangents (XYZW - 4 components per vertex) (shader-location = 4) - unsigned char *colors; // Vertex colors (RGBA - 4 components per vertex) (shader-location = 3) - unsigned short *indices;// Vertex indices (in case vertex data comes indexed) + float *vertices; // Vertex position (XYZ - 3 components per vertex) (shader-location = 0) + float *texcoords; // Vertex texture coordinates (UV - 2 components per vertex) (shader-location = 1) + float *texcoords2; // Vertex second texture coordinates (useful for lightmaps) (shader-location = 5) + float *normals; // Vertex normals (XYZ - 3 components per vertex) (shader-location = 2) + float *tangents; // Vertex tangents (XYZW - 4 components per vertex) (shader-location = 4) + unsigned char *colors; // Vertex colors (RGBA - 4 components per vertex) (shader-location = 3) + unsigned short *indices; // Vertex indices (in case vertex data comes indexed) // Animation vertex data - float *animVertices; // Animated vertex positions (after bones transformations) - float *animNormals; // Animated normals (after bones transformations) - int *boneIds; // Vertex bone ids, up to 4 bones influence by vertex (skinning) - float *boneWeights; // Vertex bone weight, up to 4 bones influence by vertex (skinning) + float *animVertices; // Animated vertex positions (after bones transformations) + float *animNormals; // Animated normals (after bones transformations) + int *boneIds; // Vertex bone ids, up to 4 bones influence by vertex (skinning) + float *boneWeights; // Vertex bone weight, up to 4 bones influence by vertex (skinning) // OpenGL identifiers - unsigned int vaoId; // OpenGL Vertex Array Object id - unsigned int *vboId; // OpenGL Vertex Buffer Objects id (default vertex data) + unsigned int vaoId; // OpenGL Vertex Array Object id + unsigned int *vboId; // OpenGL Vertex Buffer Objects id (default vertex data) } Mesh; // Shader type (generic) typedef struct Shader { - unsigned int id; // Shader program id - int *locs; // Shader locations array (MAX_SHADER_LOCATIONS) + unsigned int id; // Shader program id + int *locs; // Shader locations array (MAX_SHADER_LOCATIONS) } Shader; // Material texture map typedef struct MaterialMap { - Texture2D texture; // Material map texture - Color color; // Material map color - float value; // Material map value + Texture2D texture; // Material map texture + Color color; // Material map color + float value; // Material map value } MaterialMap; // Material type (generic) typedef struct Material { - Shader shader; // Material shader - MaterialMap *maps; // Material maps array (MAX_MATERIAL_MAPS) - float params[4]; // Material generic parameters (if required) + Shader shader; // Material shader + MaterialMap *maps; // Material maps array (MAX_MATERIAL_MAPS) + float params[4]; // Material generic parameters (if required) } Material; // Transformation properties typedef struct Transform { - Vector3 translation; // Translation - Quaternion rotation; // Rotation - Vector3 scale; // Scale + Vector3 translation; // Translation + Quaternion rotation; // Rotation + Vector3 scale; // Scale } Transform; // Bone information typedef struct BoneInfo { - char name[32]; // Bone name - int parent; // Bone parent + char name[32]; // Bone name + int parent; // Bone parent } BoneInfo; // Model type typedef struct Model { - Matrix transform; // Local transform matrix + Matrix transform; // Local transform matrix - int meshCount; // Number of meshes - int materialCount; // Number of materials - Mesh *meshes; // Meshes array - Material *materials; // Materials array - int *meshMaterial; // Mesh material number + int meshCount; // Number of meshes + int materialCount; // Number of materials + Mesh *meshes; // Meshes array + Material *materials; // Materials array + int *meshMaterial; // Mesh material number // Animation data - int boneCount; // Number of bones - BoneInfo *bones; // Bones information (skeleton) - Transform *bindPose; // Bones base transformation (pose) + int boneCount; // Number of bones + BoneInfo *bones; // Bones information (skeleton) + Transform *bindPose; // Bones base transformation (pose) } Model; // Model animation typedef struct ModelAnimation { - int boneCount; // Number of bones - int frameCount; // Number of animation frames - BoneInfo *bones; // Bones information (skeleton) - Transform **framePoses; // Poses array by frame + int boneCount; // Number of bones + int frameCount; // Number of animation frames + BoneInfo *bones; // Bones information (skeleton) + Transform **framePoses; // Poses array by frame } ModelAnimation; // Ray type (useful for raycast) typedef struct Ray { - Vector3 position; // Ray position (origin) - Vector3 direction; // Ray direction + Vector3 position; // Ray position (origin) + Vector3 direction; // Ray direction } Ray; // Raycast hit information typedef struct RayHitInfo { - bool hit; // Did the ray hit something? - float distance; // Distance to nearest hit - Vector3 position; // Position of nearest hit - Vector3 normal; // Surface normal of hit + bool hit; // Did the ray hit something? + float distance; // Distance to nearest hit + Vector3 position; // Position of nearest hit + Vector3 normal; // Surface normal of hit } RayHitInfo; // Bounding box type typedef struct BoundingBox { - Vector3 min; // Minimum vertex box-corner - Vector3 max; // Maximum vertex box-corner + Vector3 min; // Minimum vertex box-corner + Vector3 max; // Maximum vertex box-corner } BoundingBox; // Wave type, defines audio wave data typedef struct Wave { - unsigned int sampleCount; // Total number of samples (considering channels!) - unsigned int sampleRate; // Frequency (samples per second) - unsigned int sampleSize; // Bit depth (bits per sample): 8, 16, 32 (24 not supported) - unsigned int channels; // Number of channels (1-mono, 2-stereo) - void *data; // Buffer data pointer + unsigned int sampleCount; // Total number of samples (considering channels!) + unsigned int sampleRate; // Frequency (samples per second) + unsigned int sampleSize; // Bit depth (bits per sample): 8, 16, 32 (24 not supported) + unsigned int channels; // Number of channels (1-mono, 2-stereo) + void *data; // Buffer data pointer } Wave; typedef struct rAudioBuffer rAudioBuffer; @@ -430,28 +430,28 @@ typedef struct rAudioBuffer rAudioBuffer; // Audio stream type // NOTE: Useful to create custom audio streams not bound to a specific file typedef struct AudioStream { - rAudioBuffer *buffer; // Pointer to internal data used by the audio system + rAudioBuffer *buffer; // Pointer to internal data used by the audio system - unsigned int sampleRate; // Frequency (samples per second) - unsigned int sampleSize; // Bit depth (bits per sample): 8, 16, 32 (24 not supported) - unsigned int channels; // Number of channels (1-mono, 2-stereo) + unsigned int sampleRate; // Frequency (samples per second) + unsigned int sampleSize; // Bit depth (bits per sample): 8, 16, 32 (24 not supported) + unsigned int channels; // Number of channels (1-mono, 2-stereo) } AudioStream; // Sound source type typedef struct Sound { - AudioStream stream; // Audio stream - unsigned int sampleCount; // Total number of samples + AudioStream stream; // Audio stream + unsigned int sampleCount; // Total number of samples } Sound; // Music stream type (audio file streaming from memory) // NOTE: Anything longer than ~10 seconds should be streamed typedef struct Music { - AudioStream stream; // Audio stream - unsigned int sampleCount; // Total number of samples - bool looping; // Music looping enable + AudioStream stream; // Audio stream + unsigned int sampleCount; // Total number of samples + bool looping; // Music looping enable - int ctxType; // Type of music context (audio filetype) - void *ctxData; // Audio context data, depends on type + int ctxType; // Type of music context (audio filetype) + void *ctxData; // Audio context data, depends on type } Music; // Head-Mounted-Display device parameters From ded6754de42318a88d3a093ed3fae97578794a5f Mon Sep 17 00:00:00 2001 From: Milan Nikolic Date: Wed, 26 May 2021 15:07:42 +0200 Subject: [PATCH 12/38] Update raylib-go bindings (#1791) --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index 90c0b9283..7f4d0c756 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -16,7 +16,7 @@ Here it is a list with the ones I'm aware of: | raylib-d | 3.0 | [D](https://dlang.org/) | https://github.com/onroundit/raylib-d | | bindbc-raylib | 3.0 | [D](https://dlang.org/) | https://github.com/o3o/bindbc-raylib | | dray | 3.5 | [D](https://dlang.org/) | https://github.com/xdrie/dray | -| raylib-go | 3.0 | [Go](https://golang.org/) | https://github.com/gen2brain/raylib-go | +| raylib-go | 3.8-dev | [Go](https://golang.org/) | https://github.com/gen2brain/raylib-go | | raylib-goplus | 2.6-dev | [Go](https://golang.org/) | https://github.com/Lachee/raylib-goplus | | ray-go | 2.6-dev | [Go](https://golang.org/) | https://github.com/hecate-tech/ray-go | | go-raylib | 3.5 | [Go](https://golang.org/) | https://github.com/chunqian/go-raylib | From 1c7c8c682c4866b96bcd3385f0cb3993e929f496 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 26 May 2021 20:21:37 +0200 Subject: [PATCH 13/38] REVIEWED: models_skybox #1792 #1778 --- examples/models/models_skybox.c | 40 +++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/examples/models/models_skybox.c b/examples/models/models_skybox.c index bd57a34d3..2b98435ac 100644 --- a/examples/models/models_skybox.c +++ b/examples/models/models_skybox.c @@ -37,7 +37,7 @@ int main(void) Mesh cube = GenMeshCube(1.0f, 1.0f, 1.0f); Model skybox = LoadModelFromMesh(cube); - bool useHDR = false; + bool useHDR = true; // Load skybox shader and set required locations // NOTE: Some locations are automatically set at shader loading @@ -55,13 +55,15 @@ int main(void) SetShaderValue(shdrCubemap, GetShaderLocation(shdrCubemap, "equirectangularMap"), (int[1]){ 0 }, SHADER_UNIFORM_INT); char skyboxFileName[256] = { 0 }; + + Texture2D panorama; if (useHDR) { TextCopy(skyboxFileName, "resources/dresden_square_2k.hdr"); // Load HDR panorama (sphere) texture - Texture2D panorama = panorama = LoadTexture(skyboxFileName); + panorama = LoadTexture(skyboxFileName); // Generate cubemap (texture with 6 quads-cube-mapping) from panorama HDR texture // NOTE 1: New texture is generated rendering to texture, shader calculates the sphere->cube coordinates mapping @@ -69,7 +71,7 @@ int main(void) // despite texture can be successfully created.. so using PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 instead of PIXELFORMAT_UNCOMPRESSED_R32G32B32A32 skybox.materials[0].maps[MATERIAL_MAP_CUBEMAP].texture = GenTextureCubemap(shdrCubemap, panorama, 1024, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8); - UnloadTexture(panorama); // Texture not required anymore, cubemap already generated + //UnloadTexture(panorama); // Texture not required anymore, cubemap already generated } else { @@ -143,11 +145,11 @@ int main(void) DrawGrid(10, 1.0f); EndMode3D(); + + //DrawTextureEx(panorama, (Vector2){ 0, 0 }, 0.0f, 0.5f, WHITE); - if (useHDR) - DrawText(TextFormat("Panorama image from hdrihaven.com: %s", GetFileName(skyboxFileName)), 10, GetScreenHeight() - 20, 10, BLACK); - else - DrawText(TextFormat(": %s", GetFileName(skyboxFileName)), 10, GetScreenHeight() - 20, 10, BLACK); + if (useHDR) DrawText(TextFormat("Panorama image from hdrihaven.com: %s", GetFileName(skyboxFileName)), 10, GetScreenHeight() - 20, 10, BLACK); + else DrawText(TextFormat(": %s", GetFileName(skyboxFileName)), 10, GetScreenHeight() - 20, 10, BLACK); DrawFPS(10, 10); @@ -208,18 +210,32 @@ static TextureCubemap GenTextureCubemap(Shader shader, Texture2D panorama, int s }; rlViewport(0, 0, size, size); // Set viewport to current fbo dimensions + + // Activate and enable texture for drawing to cubemap faces + rlActiveTextureSlot(0); + rlEnableTexture(panorama.id); for (int i = 0; i < 6; i++) { + // Set the view matrix for the current cube face rlSetUniformMatrix(shader.locs[SHADER_LOC_MATRIX_VIEW], fboViews[i]); + + // Select the current cubemap face attachment for the fbo + // WARNING: This function by default enables->attach->disables fbo!!! rlFramebufferAttach(fbo, cubemap.id, RL_ATTACHMENT_COLOR_CHANNEL0, RL_ATTACHMENT_CUBEMAP_POSITIVE_X + i, 0); - rlEnableFramebuffer(fbo); - rlSetTexture(panorama.id); // WARNING: It must be called after enabling current framebuffer if using internal batch system! + // Load and draw a cube, it uses the current enabled texture rlClearScreenBuffers(); - DrawCubeV(Vector3Zero(), Vector3One(), WHITE); - rlDrawRenderBatchActive(); + rlLoadDrawCube(); + + // ALTERNATIVE: Try to use internal batch system to draw the cube instead of rlLoadDrawCube + // for some reason this method does not work, maybe due to cube triangles definition? normals pointing out? + // TODO: Investigate this issue... + //rlSetTexture(panorama.id); // WARNING: It must be called after enabling current framebuffer if using internal batch system! + //rlClearScreenBuffers(); + //DrawCubeV(Vector3Zero(), Vector3One(), WHITE); + //rlDrawRenderBatchActive(); } //------------------------------------------------------------------------------------------ @@ -238,7 +254,7 @@ static TextureCubemap GenTextureCubemap(Shader shader, Texture2D panorama, int s cubemap.width = size; cubemap.height = size; cubemap.mipmaps = 1; - cubemap.format = PIXELFORMAT_UNCOMPRESSED_R32G32B32; + cubemap.format = format; return cubemap; } From 470574517ab61ed71d769261cdcc136e8d0220d8 Mon Sep 17 00:00:00 2001 From: Hristo Stamenov Date: Wed, 26 May 2021 21:23:13 +0300 Subject: [PATCH 14/38] Implement vertex color attribute for GLTF and IQM (#1790) Added a simple cube with vertex colors for testing both. --- examples/models/models_gltf_model.c | 5 +- .../resources/gltf/vertex_colored_object.glb | Bin 0 -> 2512 bytes .../models/vertex_colored_object.iqm | Bin 0 -> 1800 bytes src/models.c | 56 +++++++++++++++++- 4 files changed, 58 insertions(+), 3 deletions(-) create mode 100644 examples/models/resources/gltf/vertex_colored_object.glb create mode 100644 examples/models/resources/models/vertex_colored_object.iqm diff --git a/examples/models/models_gltf_model.c b/examples/models/models_gltf_model.c index d5bd1687e..3475b3f0c 100644 --- a/examples/models/models_gltf_model.c +++ b/examples/models/models_gltf_model.c @@ -21,7 +21,7 @@ #include -#define MAX_MODELS 6 +#define MAX_MODELS 7 int main(void) { @@ -30,7 +30,7 @@ int main(void) const int screenWidth = 800; const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [models] example - model animation"); + InitWindow(screenWidth, screenHeight, "raylib [models] example - model"); // Define the camera to look into our 3d world Camera camera = { 0 }; @@ -48,6 +48,7 @@ int main(void) model[3] = LoadModel("resources/gltf/BoxAnimated.glb"); model[4] = LoadModel("resources/gltf/AnimatedTriangle.gltf"); model[5] = LoadModel("resources/gltf/AnimatedMorphCube.glb"); + model[6] = LoadModel("resources/gltf/vertex_colored_object.glb"); int currentModel = 0; diff --git a/examples/models/resources/gltf/vertex_colored_object.glb b/examples/models/resources/gltf/vertex_colored_object.glb new file mode 100644 index 0000000000000000000000000000000000000000..0b57eddbdfdd1666dbf812f5b8d6e3151fec59e4 GIT binary patch literal 2512 zcmb7E-EZ4e6u%oCYsVNXgYg<&i8s&OPhUF{lrC&qp>0r7O`xh`PU5DP61#Go!ssgH zfq#Mj0$zDSf=3>C;J-m4oO5G0ncJn?O|H+m=i_%i?y>uWy~o!HA%CwC^2-fEzHB#l zPiZ(BMLDgX(teagS(v98t<$fLvNTCYwLZAi9uA_U7iG1rPnxw8?kZO?>d=!Y8^vh? z#=>Rv%%P($0MI%sxDl=IpVA~8f{eDU&Y?-#i%`KF?udi4gSq6C4x`bLbuWAx<4y#) z=NQ{@|0svRS(=Brjl9oX?gh+f_z6$5fCb87uF=}}nS`fQfyV;p0ds{B+;C}pF16=# zuE9fl0zARBFC66t&mkJj^S9Klu1?sBKFgQ|`@*-n&)gF)PFrQ`ll5{(U0&vHh3B;;X*LW8@sIPdnCGJ|075e^<}(MB z(6_?9-PqjTY*8G6c|R7rZ#&<4o=4kJ($C>bA$%WfLC`!rw4X4BFW$`gYGd$TV$K8T zkZyCC&fW!RH>n>_uD3miPU@B{?D}+4U+J!IpWTV)BT*0Hhw0RZ3v4HU^ zzQjQNWqc_^x{NRN%jiatPl_Qzb>=&67)C%x1^F?{NN-TSxl+wB1 zYvVvXYl{$oQ|3{PWz}VFMXsfd^#R{<+1x5Ns4L@~=T@<~fCKSru*oI#&(CJ^DnrPJw)Coi6$rb>Tve*O#PFH`B~|Dv=?uaP_C z19A=aO>&F84N2XC{So<)yiab!^EP>p+$C?3HF&O(`{V|>PFCT$O5P#s zgpP#kVFll{?W?nf$?MI=<$3+0drxS`Js9@{W2^xo(?HCGp)vEiK6otDbtF)ap$!U| zKH&~}pxyB>)7p&itQepLPn>dcG18him&S1|Ie6^jIOKvBbF@?sd3*|L1IQ8M15%DI ztt4-RQ}{;L!_na{0v?XVI^5-beL0+yM~54E<2ilM;pj0hNrU6l`m(g3(H2=5W1!f&cW*zn#BkYt}Yj z?b@ofHqWhnt2$16`)(avPBpgO?t+@f2baTB>-wOphS;4^j@Pm8!j5DwhW*F-V<)l; t*^}^07!}5RJnlLBcv6@Wh^Ia0Uok7p3B>cBFUT(nO9Jt-=X&M8vJdPphlv0H literal 0 HcmV?d00001 diff --git a/src/models.c b/src/models.c index 551ee7af2..af4fd65c8 100644 --- a/src/models.c +++ b/src/models.c @@ -3458,7 +3458,7 @@ static Model LoadIQM(const char *fileName) IQM_TANGENT = 3, // NOTE: Tangents unused by default IQM_BLENDINDEXES = 4, IQM_BLENDWEIGHTS = 5, - IQM_COLOR = 6, // NOTE: Vertex colors unused by default + IQM_COLOR = 6, IQM_CUSTOM = 0x10 // NOTE: Custom vertex values unused by default }; @@ -3474,6 +3474,7 @@ static Model LoadIQM(const char *fileName) float *text = NULL; char *blendi = NULL; unsigned char *blendw = NULL; + unsigned char *color = NULL; // In case file can not be read, return an empty model if (fileDataPtr == NULL) return model; @@ -3662,6 +3663,25 @@ static Model LoadIQM(const char *fileName) } } } break; + case IQM_COLOR: + { + color = RL_MALLOC(iqmHeader->num_vertexes*4*sizeof(unsigned char)); + //fseek(iqmFile, va[i].offset, SEEK_SET); + //fread(blendw, iqmHeader->num_vertexes*4*sizeof(unsigned char), 1, iqmFile); + memcpy(color, fileDataPtr + va[i].offset, iqmHeader->num_vertexes*4*sizeof(unsigned char)); + + for (unsigned int m = 0; m < iqmHeader->num_meshes; m++) + { + model.meshes[m].colors = RL_CALLOC(model.meshes[m].vertexCount*4, sizeof(unsigned char)); + + int vCounter = 0; + for (unsigned int i = imesh[m].first_vertex*4; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*4; i++) + { + model.meshes[m].colors[vCounter] = color[i]; + vCounter++; + } + } + } break; } } @@ -4301,6 +4321,40 @@ static Model LoadGLTF(const char *fileName) TRACELOG(LOG_WARNING, "MODEL: [%s] glTF normals must be float or int", fileName); } } + else if (data->meshes[i].primitives[p].attributes[j].type == cgltf_attribute_type_color) + { + cgltf_accessor *acc = data->meshes[i].primitives[p].attributes[j].data; + model.meshes[primitiveIndex].colors = RL_MALLOC(acc->count*4*sizeof(unsigned char)); + + if (acc->component_type == cgltf_component_type_r_8u) + { + for (int a = 0; a < acc->count; a++) + { + GLTFReadValue(acc, a, model.meshes[primitiveIndex].colors + (a*4), 4, sizeof(unsigned char)); + } + } + if (acc->component_type == cgltf_component_type_r_16u) + { + TRACELOG(LOG_WARNING, "MODEL: [%s] converting glTF colors to unsigned char", fileName); + for (int a = 0; a < acc->count; a++) + { + unsigned short readValue[4]; + for (int a = 0; a < acc->count; a++) + { + GLTFReadValue(acc, a, readValue, 4, sizeof(unsigned short)); + // 257 = 65535/255 + model.meshes[primitiveIndex].colors[(a*4) + 0] = (unsigned char)(readValue[0] / 257); + model.meshes[primitiveIndex].colors[(a*4) + 1] = (unsigned char)(readValue[1] / 257); + model.meshes[primitiveIndex].colors[(a*4) + 2] = (unsigned char)(readValue[2] / 257); + model.meshes[primitiveIndex].colors[(a*4) + 3] = (unsigned char)(readValue[3] / 257); + } + } + } + else + { + TRACELOG(LOG_WARNING, "MODEL: [%s] glTF colors must be uchar or ushort", fileName); + } + } } cgltf_accessor *acc = data->meshes[i].primitives[p].indices; From beeac18eb120ef959b785d9c5832c4fd5c9fde44 Mon Sep 17 00:00:00 2001 From: Hristo Stamenov Date: Sat, 29 May 2021 14:53:00 +0300 Subject: [PATCH 15/38] Fix QuaternionToMatrix returning transposed result. (#1793) --- src/raymath.h | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/src/raymath.h b/src/raymath.h index e396d5a3a..7ab798602 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -1389,23 +1389,28 @@ RMDEF Quaternion QuaternionFromMatrix(Matrix mat) RMDEF Matrix QuaternionToMatrix(Quaternion q) { Matrix result = MatrixIdentity(); + + float a2 = q.x * q.x; + float b2 = q.y * q.y; + float c2 = q.z * q.z; + float ac = q.x * q.z; + float ab = q.x * q.y; + float bc = q.y * q.z; + float ad = q.w * q.x; + float bd = q.w * q.y; + float cd = q.w * q.z; - float a2 = 2*(q.x*q.x), b2=2*(q.y*q.y), c2=2*(q.z*q.z); //, d2=2*(q.w*q.w); + result.m0 = 1 - 2 * (b2 + c2); + result.m1 = 2 * (ab + cd); + result.m2 = 2 * (ac - bd); - float ab = 2*(q.x*q.y), ac=2*(q.x*q.z), bc=2*(q.y*q.z); - float ad = 2*(q.x*q.w), bd=2*(q.y*q.w), cd=2*(q.z*q.w); + result.m4 = 2 * (ab - cd); + result.m5 = 1 - 2 * (a2 + c2); + result.m6 = 2 * (bc + ad); - result.m0 = 1 - b2 - c2; - result.m1 = ab - cd; - result.m2 = ac + bd; - - result.m4 = ab + cd; - result.m5 = 1 - a2 - c2; - result.m6 = bc - ad; - - result.m8 = ac - bd; - result.m9 = bc + ad; - result.m10 = 1 - a2 - b2; + result.m8 = 2 * (ac + bd); + result.m9 = 2 * (bc - ad); + result.m10 = 1 - 2 * (a2 + b2); return result; } From e3ba11bead355f23caf757410f237d9292a4e19e Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 29 May 2021 13:54:05 +0200 Subject: [PATCH 16/38] Review some comments --- src/raylib.h | 102 ++++++++++++++++++++++++--------------------------- 1 file changed, 48 insertions(+), 54 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 67fde1574..d2a1685d6 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -160,13 +160,14 @@ // Temporal hacks to avoid breaking old codebases using // deprecated raylib implementation or definitions -#define FormatText TextFormat -#define LoadText LoadFileText -#define GetExtension GetFileExtension -#define GetImageData LoadImageColors -#define FILTER_POINT TEXTURE_FILTER_POINT -#define FILTER_BILINEAR TEXTURE_FILTER_BILINEAR -#define MAP_DIFFUSE MATERIAL_MAP_DIFFUSE +#define SpriteFont Font +#define FormatText TextFormat +#define LoadText LoadFileText +#define GetExtension GetFileExtension +#define GetImageData LoadImageColors +#define FILTER_POINT TEXTURE_FILTER_POINT +#define FILTER_BILINEAR TEXTURE_FILTER_BILINEAR +#define MAP_DIFFUSE MATERIAL_MAP_DIFFUSE #define UNCOMPRESSED_R8G8B8A8 PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 //---------------------------------------------------------------------------------- @@ -176,23 +177,23 @@ #if defined(__STDC__) && __STDC_VERSION__ >= 199901L #include #elif !defined(__cplusplus) && !defined(bool) - typedef enum { false, true } bool; + typedef enum bool { false, true } bool; #endif -// Vector2 type +// Vector2, 2 components typedef struct Vector2 { float x; // Vector x component float y; // Vector y component } Vector2; -// Vector3 type +// Vector3, 3 components typedef struct Vector3 { float x; // Vector x component float y; // Vector y component float z; // Vector z component } Vector3; -// Vector4 type +// Vector4, 4 components typedef struct Vector4 { float x; // Vector x component float y; // Vector y component @@ -200,10 +201,10 @@ typedef struct Vector4 { float w; // Vector w component } Vector4; -// Quaternion type, same as Vector4 +// Quaternion, 4 components (Vector4 alias) typedef Vector4 Quaternion; -// Matrix type (OpenGL style 4x4 - right handed, column major) +// Matrix, 4x4 components, column major, OpenGL style, right handed typedef struct Matrix { float m0, m4, m8, m12; // Matrix first row (4 components) float m1, m5, m9, m13; // Matrix second row (4 components) @@ -211,7 +212,7 @@ typedef struct Matrix { float m3, m7, m11, m15; // Matrix fourth row (4 components) } Matrix; -// Color type, RGBA (32bit) +// Color, 4 components, R8G8B8A8 (32bit) typedef struct Color { unsigned char r; // Color red value unsigned char g; // Color green value @@ -219,7 +220,7 @@ typedef struct Color { unsigned char a; // Color alpha value } Color; -// Rectangle type +// Rectangle, 4 components typedef struct Rectangle { float x; // Rectangle top-left corner position x float y; // Rectangle top-left corner position y @@ -227,8 +228,7 @@ typedef struct Rectangle { float height; // Rectangle height } Rectangle; -// Image type, bpp always RGBA (32bit) -// NOTE: Data stored in CPU memory (RAM) +// Image, pixel data stored in CPU memory (RAM) typedef struct Image { void *data; // Image raw data int width; // Image base width @@ -237,8 +237,7 @@ typedef struct Image { int format; // Data format (PixelFormat type) } Image; -// Texture type -// NOTE: Data stored in GPU memory +// Texture, tex data stored in GPU memory (VRAM) typedef struct Texture { unsigned int id; // OpenGL texture id int width; // Texture base width @@ -247,23 +246,23 @@ typedef struct Texture { int format; // Data format (PixelFormat type) } Texture; -// Texture2D type, same as Texture +// Texture2D, same as Texture typedef Texture Texture2D; -// TextureCubemap type, actually, same as Texture +// TextureCubemap, same as Texture typedef Texture TextureCubemap; -// RenderTexture type, for texture rendering +// RenderTexture, fbo for texture rendering typedef struct RenderTexture { unsigned int id; // OpenGL framebuffer object id Texture texture; // Color buffer attachment texture Texture depth; // Depth buffer attachment texture } RenderTexture; -// RenderTexture2D type, same as RenderTexture +// RenderTexture2D, same as RenderTexture typedef RenderTexture RenderTexture2D; -// N-Patch layout info +// NPatchInfo, n-patch layout info typedef struct NPatchInfo { Rectangle source; // Texture source rectangle int left; // Left border offset @@ -273,7 +272,7 @@ typedef struct NPatchInfo { int layout; // Layout of the n-patch: 3x3, 1x3 or 3x1 } NPatchInfo; -// Font character info +// CharInfo, font character info typedef struct CharInfo { int value; // Character value (Unicode) int offsetX; // Character offset X when drawing @@ -282,7 +281,7 @@ typedef struct CharInfo { Image image; // Character image data } CharInfo; -// Font type, includes texture and charSet array data +// Font, font texture and CharInfo array data typedef struct Font { int baseSize; // Base size (default chars height) int charsCount; // Number of characters @@ -292,9 +291,7 @@ typedef struct Font { CharInfo *chars; // Characters info data } Font; -#define SpriteFont Font // SpriteFont type fallback, defaults to Font - -// Camera type, defines a camera position/orientation in 3d space +// Camera, defines position/orientation in 3d space typedef struct Camera3D { Vector3 position; // Camera position Vector3 target; // Camera target it looks-at @@ -305,7 +302,7 @@ typedef struct Camera3D { typedef Camera3D Camera; // Camera type fallback, defaults to Camera3D -// Camera2D type, defines a 2d camera +// Camera2D, defines position/orientation in 2d space typedef struct Camera2D { Vector2 offset; // Camera offset (displacement from target) Vector2 target; // Camera target (rotation and zoom origin) @@ -313,8 +310,7 @@ typedef struct Camera2D { float zoom; // Camera zoom (scaling), should be 1.0f by default } Camera2D; -// Vertex data definning a mesh -// NOTE: Data stored in CPU memory (and GPU) +// Mesh, vertex data and vao/vbo typedef struct Mesh { int vertexCount; // Number of vertices stored in arrays int triangleCount; // Number of triangles stored (indexed or not) @@ -339,40 +335,40 @@ typedef struct Mesh { unsigned int *vboId; // OpenGL Vertex Buffer Objects id (default vertex data) } Mesh; -// Shader type (generic) +// Shader typedef struct Shader { unsigned int id; // Shader program id int *locs; // Shader locations array (MAX_SHADER_LOCATIONS) } Shader; -// Material texture map +// MaterialMap typedef struct MaterialMap { Texture2D texture; // Material map texture Color color; // Material map color float value; // Material map value } MaterialMap; -// Material type (generic) +// Material, includes shader and maps typedef struct Material { Shader shader; // Material shader MaterialMap *maps; // Material maps array (MAX_MATERIAL_MAPS) float params[4]; // Material generic parameters (if required) } Material; -// Transformation properties +// Transform, vectex transformation data typedef struct Transform { Vector3 translation; // Translation Quaternion rotation; // Rotation Vector3 scale; // Scale } Transform; -// Bone information +// Bone, skeletal animation bone typedef struct BoneInfo { char name[32]; // Bone name int parent; // Bone parent } BoneInfo; -// Model type +// Model, meshes, materials and animation data typedef struct Model { Matrix transform; // Local transform matrix @@ -388,7 +384,7 @@ typedef struct Model { Transform *bindPose; // Bones base transformation (pose) } Model; -// Model animation +// ModelAnimation typedef struct ModelAnimation { int boneCount; // Number of bones int frameCount; // Number of animation frames @@ -396,13 +392,13 @@ typedef struct ModelAnimation { Transform **framePoses; // Poses array by frame } ModelAnimation; -// Ray type (useful for raycast) +// Ray, ray for raycasting typedef struct Ray { Vector3 position; // Ray position (origin) Vector3 direction; // Ray direction } Ray; -// Raycast hit information +// RayCollision, ray hit information typedef struct RayHitInfo { bool hit; // Did the ray hit something? float distance; // Distance to nearest hit @@ -410,13 +406,13 @@ typedef struct RayHitInfo { Vector3 normal; // Surface normal of hit } RayHitInfo; -// Bounding box type +// BoundingBox typedef struct BoundingBox { Vector3 min; // Minimum vertex box-corner Vector3 max; // Maximum vertex box-corner } BoundingBox; -// Wave type, defines audio wave data +// Wave, audio wave data typedef struct Wave { unsigned int sampleCount; // Total number of samples (considering channels!) unsigned int sampleRate; // Frequency (samples per second) @@ -427,8 +423,7 @@ typedef struct Wave { typedef struct rAudioBuffer rAudioBuffer; -// Audio stream type -// NOTE: Useful to create custom audio streams not bound to a specific file +// AudioStream, custom audio stream typedef struct AudioStream { rAudioBuffer *buffer; // Pointer to internal data used by the audio system @@ -437,14 +432,13 @@ typedef struct AudioStream { unsigned int channels; // Number of channels (1-mono, 2-stereo) } AudioStream; -// Sound source type +// Sound typedef struct Sound { AudioStream stream; // Audio stream unsigned int sampleCount; // Total number of samples } Sound; -// Music stream type (audio file streaming from memory) -// NOTE: Anything longer than ~10 seconds should be streamed +// Music, audio stream, anything longer than ~10 seconds should be streamed typedef struct Music { AudioStream stream; // Audio stream unsigned int sampleCount; // Total number of samples @@ -454,7 +448,7 @@ typedef struct Music { void *ctxData; // Audio context data, depends on type } Music; -// Head-Mounted-Display device parameters +// VrDeviceInfo, Head-Mounted-Display device parameters typedef struct VrDeviceInfo { int hResolution; // Horizontal resolution in pixels int vResolution; // Vertical resolution in pixels @@ -468,7 +462,7 @@ typedef struct VrDeviceInfo { float chromaAbCorrection[4]; // Chromatic aberration correction parameters } VrDeviceInfo; -// VR Stereo rendering configuration for simulator +// VrStereoConfig, VR stereo rendering configuration for simulator typedef struct VrStereoConfig { Matrix projection[2]; // VR projection matrices (per eye) Matrix viewOffset[2]; // VR view offset matrices (per eye) @@ -1045,7 +1039,7 @@ RLAPI bool SaveFileText(const char *fileName, char *text); // Save text d RLAPI bool FileExists(const char *fileName); // Check if file exists RLAPI bool DirectoryExists(const char *dirPath); // Check if a directory path exists RLAPI bool IsFileExtension(const char *fileName, const char *ext);// Check file extension (including point: .png, .wav) -RLAPI const char *GetFileExtension(const char *fileName); // Get pointer to extension for a filename string (includes dot: ".png") +RLAPI const char *GetFileExtension(const char *fileName); // Get pointer to extension for a filename string (includes dot: '.png') RLAPI const char *GetFileName(const char *filePath); // Get pointer to filename for a path string RLAPI const char *GetFileNameWithoutExt(const char *filePath); // Get filename string without extension (uses static string) RLAPI const char *GetDirectoryPath(const char *filePath); // Get full path for a given fileName with path (uses static string) @@ -1202,7 +1196,7 @@ RLAPI Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2); RLAPI Image LoadImage(const char *fileName); // Load image from file into CPU memory (RAM) RLAPI Image LoadImageRaw(const char *fileName, int width, int height, int format, int headerSize); // Load image from RAW file data RLAPI Image LoadImageAnim(const char *fileName, int *frames); // Load image sequence from file (frames appended to image.data) -RLAPI Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, int dataSize); // Load image from memory buffer, fileType refers to extension: i.e. ".png" +RLAPI Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, int dataSize); // Load image from memory buffer, fileType refers to extension: i.e. '.png' RLAPI void UnloadImage(Image image); // Unload image from CPU memory (RAM) RLAPI bool ExportImage(Image image, const char *fileName); // Export image data to file, returns true on success RLAPI bool ExportImageAsCode(Image image, const char *fileName); // Export image as code file defining an array of bytes, returns true on success @@ -1319,7 +1313,7 @@ RLAPI Font GetFontDefault(void); RLAPI Font LoadFont(const char *fileName); // Load font from file into GPU memory (VRAM) RLAPI Font LoadFontEx(const char *fileName, int fontSize, int *fontChars, int charsCount); // Load font from file with extended parameters RLAPI Font LoadFontFromImage(Image image, Color key, int firstChar); // Load font from Image (XNA style) -RLAPI Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int dataSize, int fontSize, int *fontChars, int charsCount); // Load font from memory buffer, fileType refers to extension: i.e. ".ttf" +RLAPI Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int dataSize, int fontSize, int *fontChars, int charsCount); // Load font from memory buffer, fileType refers to extension: i.e. '.ttf' RLAPI CharInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSize, int *fontChars, int charsCount, int type); // Load font data for further use RLAPI Image GenImageFontAtlas(const CharInfo *chars, Rectangle **recs, int charsCount, int fontSize, int padding, int packMethod); // Generate image font atlas using chars info RLAPI void UnloadFontData(CharInfo *chars, int charsCount); // Unload font chars info data (RAM) @@ -1470,7 +1464,7 @@ RLAPI void SetMasterVolume(float volume); // Set mas // Wave/Sound loading/unloading functions RLAPI Wave LoadWave(const char *fileName); // Load wave data from file -RLAPI Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int dataSize); // Load wave from memory buffer, fileType refers to extension: i.e. ".wav" +RLAPI Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int dataSize); // Load wave from memory buffer, fileType refers to extension: i.e. '.wav' RLAPI Sound LoadSound(const char *fileName); // Load sound from file RLAPI Sound LoadSoundFromWave(Wave wave); // Load sound from wave data RLAPI void UpdateSound(Sound sound, const void *data, int samplesCount);// Update sound buffer with new data From c828e481fbee9846721552cb37250a93332ee115 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 29 May 2021 13:56:08 +0200 Subject: [PATCH 17/38] Review code format --- src/models.c | 12 ++++++------ src/raymath.h | 36 ++++++++++++++++++------------------ 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/models.c b/src/models.c index af4fd65c8..1e6bdebee 100644 --- a/src/models.c +++ b/src/models.c @@ -1461,9 +1461,9 @@ void UpdateModelAnimation(Model model, ModelAnimation anim, int frame) animVertex = Vector3Subtract(animVertex, inTranslation); animVertex = Vector3RotateByQuaternion(animVertex, QuaternionMultiply(outRotation, QuaternionInvert(inRotation))); animVertex = Vector3Add(animVertex, outTranslation); - model.meshes[m].animVertices[vCounter] += animVertex.x * boneWeight; - model.meshes[m].animVertices[vCounter + 1] += animVertex.y * boneWeight; - model.meshes[m].animVertices[vCounter + 2] += animVertex.z * boneWeight; + model.meshes[m].animVertices[vCounter] += animVertex.x*boneWeight; + model.meshes[m].animVertices[vCounter + 1] += animVertex.y*boneWeight; + model.meshes[m].animVertices[vCounter + 2] += animVertex.z*boneWeight; // Normals processing // NOTE: We use meshes.baseNormals (default normal) to calculate meshes.normals (animated normals) @@ -1471,9 +1471,9 @@ void UpdateModelAnimation(Model model, ModelAnimation anim, int frame) { animNormal = (Vector3){ model.meshes[m].normals[vCounter], model.meshes[m].normals[vCounter + 1], model.meshes[m].normals[vCounter + 2] }; animNormal = Vector3RotateByQuaternion(animNormal, QuaternionMultiply(outRotation, QuaternionInvert(inRotation))); - model.meshes[m].animNormals[vCounter] += animNormal.x * boneWeight; - model.meshes[m].animNormals[vCounter + 1] += animNormal.y * boneWeight; - model.meshes[m].animNormals[vCounter + 2] += animNormal.z * boneWeight; + model.meshes[m].animNormals[vCounter] += animNormal.x*boneWeight; + model.meshes[m].animNormals[vCounter + 1] += animNormal.y*boneWeight; + model.meshes[m].animNormals[vCounter + 2] += animNormal.z*boneWeight; } boneCounter += 1; } diff --git a/src/raymath.h b/src/raymath.h index 7ab798602..9d840443d 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -1390,27 +1390,27 @@ RMDEF Matrix QuaternionToMatrix(Quaternion q) { Matrix result = MatrixIdentity(); - float a2 = q.x * q.x; - float b2 = q.y * q.y; - float c2 = q.z * q.z; - float ac = q.x * q.z; - float ab = q.x * q.y; - float bc = q.y * q.z; - float ad = q.w * q.x; - float bd = q.w * q.y; - float cd = q.w * q.z; + float a2 = q.x*q.x; + float b2 = q.y*q.y; + float c2 = q.z*q.z; + float ac = q.x*q.z; + float ab = q.x*q.y; + float bc = q.y*q.z; + float ad = q.w*q.x; + float bd = q.w*q.y; + float cd = q.w*q.z; - result.m0 = 1 - 2 * (b2 + c2); - result.m1 = 2 * (ab + cd); - result.m2 = 2 * (ac - bd); + result.m0 = 1 - 2*(b2 + c2); + result.m1 = 2*(ab + cd); + result.m2 = 2*(ac - bd); - result.m4 = 2 * (ab - cd); - result.m5 = 1 - 2 * (a2 + c2); - result.m6 = 2 * (bc + ad); + result.m4 = 2*(ab - cd); + result.m5 = 1 - 2*(a2 + c2); + result.m6 = 2*(bc + ad); - result.m8 = 2 * (ac + bd); - result.m9 = 2 * (bc - ad); - result.m10 = 1 - 2 * (a2 + b2); + result.m8 = 2*(ac + bd); + result.m9 = 2*(bc - ad); + result.m10 = 1 - 2*(a2 + b2); return result; } From 0369ec9adf5c6f4838a74e29e049ebc4e3aab14f Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 30 May 2021 11:50:32 +0200 Subject: [PATCH 18/38] Some code tweaks --- src/models.c | 2 +- src/raudio.c | 6 +++--- src/text.c | 10 +++++----- src/textures.c | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/models.c b/src/models.c index 1e6bdebee..101677ca5 100644 --- a/src/models.c +++ b/src/models.c @@ -1027,7 +1027,7 @@ void DrawMeshInstanced(Mesh mesh, Material material, Matrix *transforms, int ins if (instancing) { // Create instances buffer - instanceTransforms = RL_MALLOC(instances*sizeof(float16)); + instanceTransforms = (float16 *)RL_MALLOC(instances*sizeof(float16)); // Fill buffer with instances transformations as float16 arrays for (int i = 0; i < instances; i++) instanceTransforms[i] = MatrixToFloatV(transforms[i]); diff --git a/src/raudio.c b/src/raudio.c index 639d6cd54..bb009b6e6 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -1428,13 +1428,13 @@ Music LoadMusicStreamFromMemory(const char *fileType, unsigned char* data, int d #if defined(SUPPORT_FILEFORMAT_MOD) else if (TextIsEqual(fileExtLower, ".mod")) { - jar_mod_context_t *ctxMod = RL_MALLOC(sizeof(jar_mod_context_t)); + jar_mod_context_t *ctxMod = (jar_mod_context_t *)RL_MALLOC(sizeof(jar_mod_context_t)); int result = 0; jar_mod_init(ctxMod); - // copy data to allocated memory for default UnloadMusicStream - unsigned char *newData = RL_MALLOC(dataSize); + // Copy data to allocated memory for default UnloadMusicStream + unsigned char *newData = (unsigned char *)RL_MALLOC(dataSize); int it = dataSize/sizeof(unsigned char); for (int i = 0; i < it; i++){ newData[i] = data[i]; diff --git a/src/text.c b/src/text.c index 23cad7e8d..2627e99e4 100644 --- a/src/text.c +++ b/src/text.c @@ -1272,7 +1272,7 @@ const char *TextSubtext(const char *text, int position, int length) // Replace text string // REQUIRES: strstr(), strncpy(), strcpy() -// WARNING: Internally allocated memory must be freed by the user (if return != NULL) +// WARNING: Returned buffer must be freed by the user (if return != NULL) char *TextReplace(char *text, const char *replace, const char *by) { // Sanity checks and initialization @@ -1297,14 +1297,14 @@ char *TextReplace(char *text, const char *replace, const char *by) for (count = 0; (temp = strstr(insertPoint, replace)); count++) insertPoint = temp + replaceLen; // Allocate returning string and point temp to it - temp = result = RL_MALLOC(TextLength(text) + (byLen - replaceLen)*count + 1); + temp = result = (char *)RL_MALLOC(TextLength(text) + (byLen - replaceLen)*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" + // - '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 = strstr(text, replace); diff --git a/src/textures.c b/src/textures.c index db562bd97..f46b0c9a6 100644 --- a/src/textures.c +++ b/src/textures.c @@ -1354,7 +1354,7 @@ void ImageResize(Image *image, int newWidth, int newHeight) if (fastPath) { int bytesPerPixel = GetPixelDataSize(1, 1, image->format); - unsigned char *output = RL_MALLOC(newWidth*newHeight*bytesPerPixel); + unsigned char *output = (unsigned char *)RL_MALLOC(newWidth*newHeight*bytesPerPixel); switch (image->format) { From 3c1c13b660ad2b4bcf5d0a6eaf13e3877f730d6e Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 30 May 2021 11:51:15 +0200 Subject: [PATCH 19/38] ADDED: raylib header parser --- parser/README.md | 58 ++++ parser/raylib_parser.c | 683 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 741 insertions(+) create mode 100644 parser/README.md create mode 100644 parser/raylib_parser.c diff --git a/parser/README.md b/parser/README.md new file mode 100644 index 000000000..85c212666 --- /dev/null +++ b/parser/README.md @@ -0,0 +1,58 @@ +## raylib parser + +This parser scans [`raylib.h`](../src/raylib.h) to get information about `structs`, `enums` and `functions`. +All data is separated into parts, usually as strings. The following types are used for data: + + - `struct FunctionInfo` + - `struct StructInfo` + - `struct EnumInfo` + +Check `raylib_parser.c` for details about those structs. + +## Constraints + +This parser is specifically designed to work with raylib.h, so, it has some constraints: + + - Functions are expected as a single line with the following structure: +``` + ( , ); +``` + Be careful with functions broken into several lines, it breaks the process! + + - Structures are expected as several lines with the following form: +``` + + typedef struct { + ; + ; + ; + } ; +``` + - Enums are expected as several lines with the following form: +``` + + typedef enum { + = , + , + , + + } ; +``` + +_NOTE: For enums, multiple options are supported:_ + + - If value is not provided, ( + 1) is assigned + - Value description can be provided or not + +## Additional notes + +This parser _could_ work with other C header files if mentioned constraints are followed. + +This parser **does not require `` library**, all data is parsed directly from char buffers. + +### LICENSE: zlib/libpng + +raylib-parser is licensed under an unmodified zlib/libpng license, which is an OSI-certified, +BSD-like license that allows static linking with closed source software: + +Copyright (c) 2021 Ramon Santamaria (@raysan5) \ No newline at end of file diff --git a/parser/raylib_parser.c b/parser/raylib_parser.c new file mode 100644 index 000000000..59c438852 --- /dev/null +++ b/parser/raylib_parser.c @@ -0,0 +1,683 @@ +/********************************************************************************************** + + raylib parser - raylib header parser + + This parser scans raylib.h to get information about structs, enums and functions. + All data is separated into parts, usually as strings. The following types are used for data: + + - struct FunctionInfo + - struct StructInfo + - struct EnumInfo + + CONSTRAINTS: + + This parser is specifically designed to work with raylib.h, so, it has some constraints: + + - Functions are expected as a single line with the following structure: + + ( , ); + + Be careful with functions broken into several lines, it breaks the process! + + - Structures are expected as several lines with the following form: + + + typedef struct { + ; + ; + ; + } ; + + - Enums are expected as several lines with the following form: + + + typedef enum { + = , + , + , + + } ; + + NOTE: Multiple options are supported: + - If value is not provided, ( + 1) is assigned + - Value description can be provided or not + + This parser could work with other C header files if mentioned constraints are followed. + + This parser does not require library, all data is parsed directly from char buffers. + + LICENSE: zlib/libpng + + raylib-parser is licensed under an unmodified zlib/libpng license, which is an OSI-certified, + BSD-like license that allows static linking with closed source software: + + Copyright (c) 2021 Ramon Santamaria (@raysan5) + +**********************************************************************************************/ + +#include // Required for: malloc(), calloc(), realloc(), free(), atoi(), strtol() +#include // Required for: printf(), fopen(), fseek(), ftell(), fread(), fclose() +#include // Required for: bool + +#define MAX_FUNCS_TO_PARSE 512 // Maximum number of functions to parse +#define MAX_STRUCTS_TO_PARSE 64 // Maximum number of structures to parse +#define MAX_ENUMS_TO_PARSE 64 // Maximum number of enums to parse + +#define MAX_LINE_LENGTH 512 // Maximum length of one line (including comments) +#define MAX_STRUCT_LINE_LENGTH 2048 // Maximum length of one struct (multiple lines) + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +// Function info data +typedef struct FunctionInfo { + char name[64]; // Function name + char retType[32]; // Return value type + int paramCount; // Number of function parameters + char paramType[12][32]; // Parameters type (max: 12 parameters) + char paramName[12][32]; // Parameters name (max: 12 parameters) + char desc[128]; // Function description (comment at the end) +} FunctionInfo; + +// Struct info data +typedef struct StructInfo { + char name[64]; // Struct name + char desc[64]; // Struct type description + int fieldCount; // Number of fields in the struct + char fieldType[16][32]; // Field type (max: 16 fields) + char fieldName[16][32]; // Field name (max: 16 fields) + char fieldDesc[16][128]; // Field description (max: 16 fields) +} StructInfo; + +// Enum info data +typedef struct EnumInfo { + char name[64]; // Enum name + char desc[64]; // Enum description + int valueCount; // Number of values in enumerator + char valueName[128][64]; // Value name definition (max: 128 values) + int valueInt[128]; // Value integer (max: 128 values) +} EnumInfo; + +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- +char *LoadFileText(const char *fileName, int *length); +char **GetTextLines(const char *buffer, int length, int *linesCount); +void GetDataTypeAndName(const char *typeName, int typeNameLen, char *type, char *name); +bool IsTextEqual(const char *text1, const char *text2, unsigned int count); +void MemoryCopy(void *dest, const void *src, unsigned int count); + +// Main entry point +int main() +{ + int length = 0; + char *buffer = LoadFileText("../src/raylib.h", &length); + + // Preprocess buffer to get separate lines + // NOTE: GetTextLines() also removes leading spaces/tabs + int linesCount = 0; + char **lines = GetTextLines(buffer, length, &linesCount); + + // Print buffer lines + //for (int i = 0; i < linesCount; i++) printf("_%s_\n", lines[i]); + + // Function lines pointers, selected from buffer "lines" + int funcCount = 0; + char **funcLines = (char **)malloc(MAX_FUNCS_TO_PARSE*sizeof(char *)); + + // Structs data (multiple lines), selected from "buffer" + int structCount = 0; + char **structLines = (char **)malloc(MAX_STRUCTS_TO_PARSE*sizeof(char *)); + for (int i = 0; i < MAX_STRUCTS_TO_PARSE; i++) structLines[i] = (char *)calloc(MAX_STRUCT_LINE_LENGTH, sizeof(char)); + + // Enums lines pointers, selected from buffer "lines" + int enumCount = 0; + int *enumLines = (int *)malloc(MAX_ENUMS_TO_PARSE*sizeof(int)); + + // Prepare required lines for parsing + //-------------------------------------------------------------------------------------------------- + + // Read function lines + for (int i = 0; i < linesCount; i++) + { + // Read function line (starting with "RLAPI") + if (IsTextEqual(lines[i], "RLAPI", 5)) + { + // Keep a pointer to the function line + funcLines[funcCount] = lines[i]; + funcCount++; + } + } + + // Print function lines + //for (int i = 0; i < funcCount; i++) printf("%s\n", funcLines[i]); + + // Read structs data (multiple lines, read directly from buffer) + // TODO: Parse structs data from "lines" instead of "buffer" -> Easier to get struct definition + for (int i = 0; i < length; i++) + { + // Read struct data (starting with "typedef struct", ending with '} ... ;') + // NOTE: We read it directly from buffer + if (IsTextEqual(buffer + i, "typedef struct", 14)) + { + int j = 0; + bool validStruct = false; + + // WARNING: Typedefs between types: typedef Vector4 Quaternion; + + for (int c = 0; c < 128; c++) + { + if (buffer[i + c] == '{') + { + validStruct = true; + break; + } + else if (buffer[i + j] == ';') + { + // Not valid struct: + // i.e typedef struct rAudioBuffer rAudioBuffer; -> Typedef and forward declaration + i += c; + break; + } + } + + if (validStruct) + { + while (buffer[i + j] != '}') + { + structLines[structCount][j] = buffer[i + j]; + j++; + } + + while (buffer[i + j] != '}') + { + structLines[structCount][j] = buffer[i + j]; + j++; + } + + while (buffer[i + j] != '\n') + { + structLines[structCount][j] = buffer[i + j]; + j++; + } + + i += j; + structCount++; + } + } + } + + // Read enum lines + for (int i = 0; i < linesCount; i++) + { + // Read function line (starting with "RLAPI") + if (IsTextEqual(lines[i], "typedef enum {", 14)) + { + // Keep the line position in the array of lines, + // so, we can scan that position and following lines + enumLines[enumCount] = i; + enumCount++; + } + } + + // At this point we have all raylib structs, enums, functions lines data to start parsing + + free(buffer); // Unload text buffer + + // Parsing raylib data + //-------------------------------------------------------------------------------------------------- + + // Structs info data + StructInfo *structs = (StructInfo *)calloc(MAX_STRUCTS_TO_PARSE, sizeof(StructInfo)); + + for (int i = 0; i < structCount; i++) + { + int structLineOffset = 0; + + // Get struct name: typedef struct name { + for (int c = 15; c < 64 + 15; c++) + { + if (structLines[i][c] == '{') + { + structLineOffset = c + 2; + + MemoryCopy(structs[i].name, &structLines[i][15], c - 15 - 1); + break; + } + } + + // Get struct fields and count them -> fields finish with ; + int j = 0; + while (structLines[i][structLineOffset + j] != '}') + { + // WARNING: Some structs have empty spaces and comments -> OK, processed + + int fieldStart = 0; + if ((structLines[i][structLineOffset + j] != ' ') && (structLines[i][structLineOffset + j] != '\n')) fieldStart = structLineOffset + j; + + if (fieldStart != 0) + { + // Scan one field line + int c = 0; + int fieldEndPos = 0; + char fieldLine[256] = { 0 }; + + while (structLines[i][structLineOffset + j] != '\n') + { + if (structLines[i][structLineOffset + j] == ';') fieldEndPos = c; + fieldLine[c] = structLines[i][structLineOffset + j]; + c++; j++; + } + + if (fieldLine[0] != '/') // Field line is not a comment + { + //printf("Struct field: %s_\n", fieldLine); // OK! + + // Get struct field type and name + GetDataTypeAndName(fieldLine, fieldEndPos, structs[i].fieldType[structs[i].fieldCount], structs[i].fieldName[structs[i].fieldCount]); + + // Get the field description + // We start skipping spaces in front of description comment + int descStart = fieldEndPos; + while ((fieldLine[descStart] != '/') && (fieldLine[descStart] != '\0')) descStart++; + + int k = 0; + while ((fieldLine[descStart + k] != '\0') && (fieldLine[descStart + k] != '\n')) + { + structs[i].fieldDesc[structs[i].fieldCount][k] = fieldLine[descStart + k]; + k++; + } + + structs[i].fieldCount++; + } + } + + j++; + } + + } + + for (int i = 0; i < MAX_STRUCTS_TO_PARSE; i++) free(structLines[i]); + free(structLines); + + // Enum info data + EnumInfo *enums = (EnumInfo *)calloc(MAX_ENUMS_TO_PARSE, sizeof(EnumInfo)); + + for (int i = 0; i < enumCount; i++) + { + // TODO: Get enum description from lines[enumLines[i] - 1] + + for (int j = 1; j < 256; j++) // Maximum number of lines following enum first line + { + char *linePtr = lines[enumLines[i] + j]; + + if ((linePtr[0] >= 'A') && (linePtr[0] <= 'Z')) + { + // Parse enum value line, possible options: + //ENUM_VALUE_NAME, + //ENUM_VALUE_NAME + //ENUM_VALUE_NAME = 99 + //ENUM_VALUE_NAME = 99, + //ENUM_VALUE_NAME = 0x00000040, // Value description + + // We start reading the value name + int c = 0; + while ((linePtr[c] != ',') && + (linePtr[c] != ' ') && + (linePtr[c] != '=') && + (linePtr[c] != '\0')) { enums[i].valueName[enums[i].valueCount][c] = linePtr[c]; c++; } + + // After the name we can have: + // '=' -> value is provided + // ',' -> value is equal to previous + 1, there could be a description if not '\0' + // ' ' -> value is equal to previous + 1, there could be a description if not '\0' + // '\0' -> value is equal to previous + 1 + + // Let's start checking if the line is not finished + if ((linePtr[c] != ',') && (linePtr[c] != '\0')) + { + // Two options: + // '=' -> value is provided + // ' ' -> value is equal to previous + 1, there could be a description if not '\0' + bool foundValue = false; + while (linePtr[c] != '\0') + { + if (linePtr[c] == '=') { foundValue = true; break; } + c++; + } + + if (foundValue) + { + if (linePtr[c + 1] == ' ') c += 2; + else c++; + + // Parse integer value + int n = 0; + char integer[16] = { 0 }; + + while ((linePtr[c] != ',') && (linePtr[c] != ' ') && (linePtr[c] != '\0')) + { + integer[n] = linePtr[c]; + c++; n++; + } + + if (integer[1] == 'x') enums[i].valueInt[enums[i].valueCount] = (int)strtol(integer, NULL, 16); + else enums[i].valueInt[enums[i].valueCount] = atoi(integer); + } + else enums[i].valueInt[enums[i].valueCount] = (enums[i].valueInt[enums[i].valueCount - 1] + 1); + + // TODO: Parse value description if any + } + else enums[i].valueInt[enums[i].valueCount] = (enums[i].valueInt[enums[i].valueCount - 1] + 1); + + enums[i].valueCount++; + } + else if (linePtr[0] == '}') + { + // Get enum name from typedef + int c = 0; + while (linePtr[2 + c] != ';') { enums[i].name[c] = linePtr[2 + c]; c++; } + + break; // Enum ended, break for() loop + } + } + } + + // Functions info data + FunctionInfo *funcs = (FunctionInfo *)calloc(MAX_FUNCS_TO_PARSE, sizeof(FunctionInfo)); + + for (int i = 0; i < funcCount; i++) + { + int funcParamsStart = 0; + int funcEnd = 0; + + // Get return type and function name from func line + for (int c = 0; (c < MAX_LINE_LENGTH) && (funcLines[i][c] != '\n'); c++) + { + if (funcLines[i][c] == '(') // Starts function parameters + { + funcParamsStart = c + 1; + + // At this point we have function return type and function name + char funcRetTypeName[128] = { 0 }; + int funcRetTypeNameLen = c - 6; // Substract "RLAPI " + MemoryCopy(funcRetTypeName, &funcLines[i][6], funcRetTypeNameLen); + + GetDataTypeAndName(funcRetTypeName, funcRetTypeNameLen, funcs[i].retType, funcs[i].name); + break; + } + } + + // Get parameters from func line + for (int c = funcParamsStart; c < MAX_LINE_LENGTH; c++) + { + if (funcLines[i][c] == ',') // Starts function parameters + { + // Get parameter type + name, extract info + char funcParamTypeName[128] = { 0 }; + int funcParamTypeNameLen = c - funcParamsStart; + MemoryCopy(funcParamTypeName, &funcLines[i][funcParamsStart], funcParamTypeNameLen); + + GetDataTypeAndName(funcParamTypeName, funcParamTypeNameLen, funcs[i].paramType[funcs[i].paramCount], funcs[i].paramName[funcs[i].paramCount]); + + funcParamsStart = c + 1; + if (funcLines[i][c + 1] == ' ') funcParamsStart += 1; + funcs[i].paramCount++; // Move to next parameter + } + else if (funcLines[i][c] == ')') + { + funcEnd = c + 2; + + // Check if previous word is void + if ((funcLines[i][c - 4] == 'v') && (funcLines[i][c - 3] == 'o') && (funcLines[i][c - 2] == 'i') && (funcLines[i][c - 1] == 'd')) break; + + // Get parameter type + name, extract info + char funcParamTypeName[128] = { 0 }; + int funcParamTypeNameLen = c - funcParamsStart; + MemoryCopy(funcParamTypeName, &funcLines[i][funcParamsStart], funcParamTypeNameLen); + + GetDataTypeAndName(funcParamTypeName, funcParamTypeNameLen, funcs[i].paramType[funcs[i].paramCount], funcs[i].paramName[funcs[i].paramCount]); + + funcs[i].paramCount++; // Move to next parameter + break; + } + } + + // Get function description + for (int c = funcEnd; c < MAX_LINE_LENGTH; c++) + { + if (funcLines[i][c] == '/') + { + MemoryCopy(funcs[i].desc, &funcLines[i][c], 127); // WARNING: Size could be too long for funcLines[i][c]? + break; + } + } + } + + for (int i = 0; i < linesCount; i++) free(lines[i]); + free(lines); + free(funcLines); + + // At this point, all raylib data has been parsed! + //----------------------------------------------------------------------------------------- + // structs[] -> We have all the structs decomposed into pieces for further analysis + // enums[] -> We have all the enums decomposed into pieces for further analysis + // funcs[] -> We have all the functions decomposed into pieces for further analysis + + // Print structs info + printf("\nStructures found: %i\n\n", structCount); + for (int i = 0; i < structCount; i++) + { + printf("Struct %02i: %s (%i fields)\n", i + 1, structs[i].name, structs[i].fieldCount); + //printf("Description: %s\n", structs[i].desc); + for (int f = 0; f < structs[i].fieldCount; f++) printf(" Fields %i: %s %s %s\n", f + 1, structs[i].fieldType[f], structs[i].fieldName[f], structs[i].fieldDesc[f]); + } + + // Print enums info + printf("\nEnums found: %i\n\n", enumCount); + for (int i = 0; i < enumCount; i++) + { + printf("Enum %02i: %s (%i values)\n", i + 1, enums[i].name, enums[i].valueCount); + //printf("Description: %s\n", enums[i].desc); + for (int e = 0; e < enums[i].valueCount; e++) printf(" Value %s: %i\n", enums[i].valueName[e], enums[i].valueInt[e]); + } + + // Print function info + printf("\nFunctions found: %i\n\n", funcCount); + for (int i = 0; i < funcCount; i++) + { + printf("Function %03i: %s() (%i input parameters)\n", i + 1, funcs[i].name, funcs[i].paramCount); + printf(" Description: %s\n", funcs[i].desc); + printf(" Return type: %s\n", funcs[i].retType); + for (int p = 0; p < funcs[i].paramCount; p++) printf(" Param %i: %s (type: %s)\n", p + 1, funcs[i].paramName[p], funcs[i].paramType[p]); + if (funcs[i].paramCount == 0) printf(" No input parameters\n"); + } + + free(funcs); + free(structs); + free(enums); +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition +//---------------------------------------------------------------------------------- + +// Load text data from file, returns a '\0' terminated string +// NOTE: text chars array should be freed manually +char *LoadFileText(const char *fileName, int *length) +{ + char *text = NULL; + + if (fileName != NULL) + { + FILE *file = fopen(fileName, "rt"); + + if (file != NULL) + { + // WARNING: When reading a file as 'text' file, + // text mode causes carriage return-linefeed translation... + // ...but using fseek() should return correct byte-offset + fseek(file, 0, SEEK_END); + int size = ftell(file); + fseek(file, 0, SEEK_SET); + + if (size > 0) + { + *length = size; + + text = (char *)calloc((size + 1), sizeof(char)); + unsigned int count = (unsigned int)fread(text, sizeof(char), size, file); + + // WARNING: \r\n is converted to \n on reading, so, + // read bytes count gets reduced by the number of lines + if (count < (unsigned int)size) text = realloc(text, count + 1); + + // Zero-terminate the string + text[count] = '\0'; + } + + fclose(file); + } + } + + return text; +} + +// Get all lines from a text buffer (expecting lines ending with '\n') +char **GetTextLines(const char *buffer, int length, int *linesCount) +{ + //#define MAX_LINE_LENGTH 512 + + // Get the number of lines in the text + int count = 0; + for (int i = 0; i < length; i++) if (buffer[i] == '\n') count++; + + //printf("Number of text lines in buffer: %i\n", count); + + // Allocate as many pointers as lines + char **lines = (char **)malloc(count*sizeof(char **)); + + char *bufferPtr = (char *)buffer; + + for (int i = 0; (i < count) || (bufferPtr[0] != '\0'); i++) + { + lines[i] = (char *)calloc(MAX_LINE_LENGTH, sizeof(char)); + + // Remove line leading spaces + // Find last index of space/tab character + int index = 0; + while ((bufferPtr[index] == ' ') || (bufferPtr[index] == '\t')) index++; + + int j = 0; + while (bufferPtr[index + j] != '\n') + { + lines[i][j] = bufferPtr[index + j]; + j++; + } + + bufferPtr += (index + j + 1); + } + + *linesCount = count; + return lines; +} + +// Get data type and name from a string containing both +// NOTE: Useful to parse function parameters and struct fields +void GetDataTypeAndName(const char *typeName, int typeNameLen, char *type, char *name) +{ + for (int k = typeNameLen; k > 0; k--) + { + if (typeName[k] == ' ') + { + // Function name starts at this point (and ret type finishes at this point) + MemoryCopy(type, typeName, k); + MemoryCopy(name, typeName + k + 1, typeNameLen - k - 1); + break; + } + else if (typeName[k] == '*') + { + MemoryCopy(type, typeName, k + 1); + MemoryCopy(name, typeName + k + 1, typeNameLen - k - 1); + break; + } + } +} + +// Custom memcpy() to avoid +void MemoryCopy(void *dest, const void *src, unsigned int count) +{ + char *srcPtr = (char *)src; + char *destPtr = (char *)dest; + + for (unsigned int i = 0; i < count; i++) destPtr[i] = srcPtr[i]; +} + +// Compare two text strings, requires number of characters to compare +bool IsTextEqual(const char *text1, const char *text2, unsigned int count) +{ + bool result = true; + + for (unsigned int i = 0; i < count; i++) + { + if (text1[i] != text2[i]) + { + result = false; + break; + } + } + + return result; +} + +/* +// Replace text string +// REQUIRES: strlen(), strstr(), strncpy(), strcpy() -> TODO: Replace by custom implementations! +// WARNING: Returned buffer must be freed by the user (if return != NULL) +char *TextReplace(char *text, const char *replace, const char *by) +{ + // Sanity checks and initialization + if (!text || !replace || !by) return NULL; + + char *result; + + char *insertPoint; // Next insert point + char *temp; // Temp pointer + int replaceLen; // Replace string length of (the string to remove) + int byLen; // Replacement length (the string to replace replace by) + int lastReplacePos; // Distance between replace and end of last replace + int count; // Number of replacements + + replaceLen = strlen(replace); + if (replaceLen == 0) return NULL; // Empty replace causes infinite loop during count + + byLen = strlen(by); + + // Count the number of replacements needed + insertPoint = text; + for (count = 0; (temp = strstr(insertPoint, replace)); count++) insertPoint = temp + replaceLen; + + // Allocate returning string and point temp to it + temp = result = (char *)malloc(strlen(text) + (byLen - replaceLen)*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 = strstr(text, replace); + lastReplacePos = (int)(insertPoint - text); + temp = strncpy(temp, text, lastReplacePos) + lastReplacePos; + temp = strcpy(temp, by) + byLen; + text += lastReplacePos + replaceLen; // Move to next "end of replace" + } + + // Copy remaind text part after replacement to result (pointed by moving temp) + strcpy(temp, text); + + return result; +} +*/ \ No newline at end of file From a178a2170bb821fbee135c17fc166733caa19bda Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 30 May 2021 13:34:02 +0200 Subject: [PATCH 20/38] Structs tweaks for consistency --- parser/raylib_parser.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/parser/raylib_parser.c b/parser/raylib_parser.c index 59c438852..4655fd14a 100644 --- a/parser/raylib_parser.c +++ b/parser/raylib_parser.c @@ -72,11 +72,11 @@ // Function info data typedef struct FunctionInfo { char name[64]; // Function name + char desc[128]; // Function description (comment at the end) char retType[32]; // Return value type int paramCount; // Number of function parameters char paramType[12][32]; // Parameters type (max: 12 parameters) char paramName[12][32]; // Parameters name (max: 12 parameters) - char desc[128]; // Function description (comment at the end) } FunctionInfo; // Struct info data @@ -96,6 +96,7 @@ typedef struct EnumInfo { int valueCount; // Number of values in enumerator char valueName[128][64]; // Value name definition (max: 128 values) int valueInt[128]; // Value integer (max: 128 values) + char valueDesc[128][64]; // Value description (max: 128 values) } EnumInfo; //---------------------------------------------------------------------------------- From 71995d52b36c86b09f064953fb3dd2d2fd60c704 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 30 May 2021 18:02:06 +0200 Subject: [PATCH 21/38] REVIEWED: exit() on LOG_FATAL instead of LOG_ERROR #1796 --- src/core.c | 28 +++++++++++++++++----------- src/raudio.c | 32 +++++++++++++++++--------------- src/raylib.h | 15 ++++++++------- src/rlgl.h | 19 ++++++++++--------- src/utils.c | 2 +- 5 files changed, 53 insertions(+), 43 deletions(-) diff --git a/src/core.c b/src/core.c index e84d82c49..1289f54d5 100644 --- a/src/core.c +++ b/src/core.c @@ -652,7 +652,7 @@ void InitWindow(int width, int height, const char *title) #if defined(PLATFORM_UWP) if (!UWPIsConfigured()) { - TRACELOG(LOG_ERROR, "UWP Functions have not been set yet, please set these before initializing raylib!"); + TRACELOG(LOG_FATAL, "UWP Functions have not been set yet, please set these before initializing raylib!"); return; } #endif @@ -736,7 +736,12 @@ void InitWindow(int width, int height, const char *title) // NOTE: returns true if window and graphic device has been initialized successfully CORE.Window.ready = InitGraphicsDevice(width, height); - if (!CORE.Window.ready) return; + // If graphic device is no properly initialized, we end program + if (!CORE.Window.ready) + { + TRACELOG(LOG_FATAL, "Failed to initialize Graphic Device"); + return; + } // Init hi-res timer InitTimer(); @@ -4914,6 +4919,7 @@ static void SwapBuffers(void) { gbm_surface_release_buffer(CORE.Window.gbmSurface, CORE.Window.prevBO); } + CORE.Window.prevBO = bo; #endif // PLATFORM_DRM #endif // PLATFORM_ANDROID || PLATFORM_RPI || PLATFORM_DRM || PLATFORM_UWP @@ -6268,15 +6274,15 @@ bool UWPIsConfigured() { bool pass = true; - if (uwpQueryTimeFunc == NULL) { TRACELOG(LOG_ERROR, "UWP: UWPSetQueryTimeFunc() must be called with a valid function before InitWindow()"); pass = false; } - if (uwpSleepFunc == NULL) { TRACELOG(LOG_ERROR, "UWP: UWPSetSleepFunc() must be called with a valid function before InitWindow()"); pass = false; } - if (uwpDisplaySizeFunc == NULL) { TRACELOG(LOG_ERROR, "UWP: UWPSetDisplaySizeFunc() must be called with a valid function before InitWindow()"); pass = false; } - if (uwpMouseLockFunc == NULL) { TRACELOG(LOG_ERROR, "UWP: UWPSetMouseLockFunc() must be called with a valid function before InitWindow()"); pass = false; } - if (uwpMouseUnlockFunc == NULL) { TRACELOG(LOG_ERROR, "UWP: UWPSetMouseUnlockFunc() must be called with a valid function before InitWindow()"); pass = false; } - if (uwpMouseShowFunc == NULL) { TRACELOG(LOG_ERROR, "UWP: UWPSetMouseShowFunc() must be called with a valid function before InitWindow()"); pass = false; } - if (uwpMouseHideFunc == NULL) { TRACELOG(LOG_ERROR, "UWP: UWPSetMouseHideFunc() must be called with a valid function before InitWindow()"); pass = false; } - if (uwpMouseSetPosFunc == NULL) { TRACELOG(LOG_ERROR, "UWP: UWPSetMouseSetPosFunc() must be called with a valid function before InitWindow()"); pass = false; } - if (uwpCoreWindow == NULL) { TRACELOG(LOG_ERROR, "UWP: A pointer to the UWP core window must be set before InitWindow()"); pass = false; } + if (uwpQueryTimeFunc == NULL) { TRACELOG(LOG_WARNING, "UWP: UWPSetQueryTimeFunc() must be called with a valid function before InitWindow()"); pass = false; } + if (uwpSleepFunc == NULL) { TRACELOG(LOG_WARNING, "UWP: UWPSetSleepFunc() must be called with a valid function before InitWindow()"); pass = false; } + if (uwpDisplaySizeFunc == NULL) { TRACELOG(LOG_WARNING, "UWP: UWPSetDisplaySizeFunc() must be called with a valid function before InitWindow()"); pass = false; } + if (uwpMouseLockFunc == NULL) { TRACELOG(LOG_WARNING, "UWP: UWPSetMouseLockFunc() must be called with a valid function before InitWindow()"); pass = false; } + if (uwpMouseUnlockFunc == NULL) { TRACELOG(LOG_WARNING, "UWP: UWPSetMouseUnlockFunc() must be called with a valid function before InitWindow()"); pass = false; } + if (uwpMouseShowFunc == NULL) { TRACELOG(LOG_WARNING, "UWP: UWPSetMouseShowFunc() must be called with a valid function before InitWindow()"); pass = false; } + if (uwpMouseHideFunc == NULL) { TRACELOG(LOG_WARNING, "UWP: UWPSetMouseHideFunc() must be called with a valid function before InitWindow()"); pass = false; } + if (uwpMouseSetPosFunc == NULL) { TRACELOG(LOG_WARNING, "UWP: UWPSetMouseSetPosFunc() must be called with a valid function before InitWindow()"); pass = false; } + if (uwpCoreWindow == NULL) { TRACELOG(LOG_WARNING, "UWP: A pointer to the UWP core window must be set before InitWindow()"); pass = false; } return pass; } diff --git a/src/raudio.c b/src/raudio.c index bb009b6e6..fcc914be4 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -276,25 +276,27 @@ typedef struct tagBITMAPINFOHEADER { // NOTE: Depends on data structure provided by the library // in charge of reading the different file types typedef enum { - MUSIC_AUDIO_NONE = 0, - MUSIC_AUDIO_WAV, - MUSIC_AUDIO_OGG, - MUSIC_AUDIO_FLAC, - MUSIC_AUDIO_MP3, - MUSIC_MODULE_XM, - MUSIC_MODULE_MOD + MUSIC_AUDIO_NONE = 0, // No audio context loaded + MUSIC_AUDIO_WAV, // WAV audio context + MUSIC_AUDIO_OGG, // OGG audio context + MUSIC_AUDIO_FLAC, // FLAC audio context + MUSIC_AUDIO_MP3, // MP3 audio context + MUSIC_MODULE_XM, // XM module audio context + MUSIC_MODULE_MOD // MOD module audio context } MusicContextType; #if defined(RAUDIO_STANDALONE) +// Trace log level +// NOTE: Organized by priority level typedef enum { - LOG_ALL, - LOG_TRACE, - LOG_DEBUG, - LOG_INFO, - LOG_WARNING, - LOG_ERROR, - LOG_FATAL, - LOG_NONE + LOG_ALL = 0, // Display all logs + LOG_TRACE, // Trace logging, intended for internal use only + LOG_DEBUG, // Debug logging, used for internal debugging, it should be disabled on release builds + LOG_INFO, // Info logging, used for program execution info + LOG_WARNING, // Warning logging, used on recoverable failures + LOG_ERROR, // Error logging, used on unrecoverable failures + LOG_FATAL, // Fatal logging, used to abort program: exit(EXIT_FAILURE) + LOG_NONE // Disable logging } TraceLogLevel; #endif diff --git a/src/raylib.h b/src/raylib.h index d2a1685d6..63d1bdf62 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -498,14 +498,15 @@ typedef enum { } ConfigFlags; // Trace log level +// NOTE: Organized by priority level typedef enum { LOG_ALL = 0, // Display all logs - LOG_TRACE, - LOG_DEBUG, - LOG_INFO, - LOG_WARNING, - LOG_ERROR, - LOG_FATAL, + LOG_TRACE, // Trace logging, intended for internal use only + LOG_DEBUG, // Debug logging, used for internal debugging, it should be disabled on release builds + LOG_INFO, // Info logging, used for program execution info + LOG_WARNING, // Warning logging, used on recoverable failures + LOG_ERROR, // Error logging, used on unrecoverable failures + LOG_FATAL, // Fatal logging, used to abort program: exit(EXIT_FAILURE) LOG_NONE // Disable logging } TraceLogLevel; @@ -1015,7 +1016,7 @@ RLAPI int GetRandomValue(int min, int max); // Returns a r RLAPI void TakeScreenshot(const char *fileName); // Takes a screenshot of current screen (filename extension defines format) RLAPI void SetConfigFlags(unsigned int flags); // Setup init configuration flags (view FLAGS) -RLAPI void TraceLog(int logLevel, const char *text, ...); // Show trace log messages (LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR) +RLAPI void TraceLog(int logLevel, const char *text, ...); // Show trace log messages (LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR...) RLAPI void SetTraceLogLevel(int logLevel); // Set the current threshold (minimum) log level RLAPI void *MemAlloc(int size); // Internal memory allocator RLAPI void *MemRealloc(void *ptr, int size); // Internal memory reallocator diff --git a/src/rlgl.h b/src/rlgl.h index a40c80477..ba54a259e 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -329,16 +329,17 @@ typedef enum { int *locs; // Shader locations array (MAX_SHADER_LOCATIONS) } Shader; - // TraceLog message types + // Trace log level + // NOTE: Organized by priority level typedef enum { - LOG_ALL, - LOG_TRACE, - LOG_DEBUG, - LOG_INFO, - LOG_WARNING, - LOG_ERROR, - LOG_FATAL, - LOG_NONE + LOG_ALL = 0, // Display all logs + LOG_TRACE, // Trace logging, intended for internal use only + LOG_DEBUG, // Debug logging, used for internal debugging, it should be disabled on release builds + LOG_INFO, // Info logging, used for program execution info + LOG_WARNING, // Warning logging, used on recoverable failures + LOG_ERROR, // Error logging, used on unrecoverable failures + LOG_FATAL, // Fatal logging, used to abort program: exit(EXIT_FAILURE) + LOG_NONE // Disable logging } TraceLogLevel; // Texture formats (support depends on OpenGL version) diff --git a/src/utils.c b/src/utils.c index c21d23181..2df0a9397 100644 --- a/src/utils.c +++ b/src/utils.c @@ -155,7 +155,7 @@ void TraceLog(int logType, const char *text, ...) va_end(args); - if (logType == LOG_ERROR) exit(1); // If error, exit program + if (logType == LOG_FATAL) exit(EXIT_FAILURE); // If fatal logging, exit program #endif // SUPPORT_TRACELOG } From cc4de1d1f9073dc75a4d79cdc2551b5e2e1b5215 Mon Sep 17 00:00:00 2001 From: Julianiolo <50519317+Julianiolo@users.noreply.github.com> Date: Sun, 30 May 2021 18:33:38 +0200 Subject: [PATCH 22/38] note (#1797) --- src/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core.c b/src/core.c index 1289f54d5..5d7b0eca3 100644 --- a/src/core.c +++ b/src/core.c @@ -3548,7 +3548,7 @@ static bool InitGraphicsDevice(int width, int height) CORE.Window.display.width = mode->width; CORE.Window.display.height = mode->height; - // Screen size security check + // Set screen width/height to the display width/height if they are 0 if (CORE.Window.screen.width == 0) CORE.Window.screen.width = CORE.Window.display.width; if (CORE.Window.screen.height == 0) CORE.Window.screen.height = CORE.Window.display.height; #endif // PLATFORM_DESKTOP From 89e734de6b720b54669464361e0c0ca1ef0f7369 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 30 May 2021 22:31:14 +0200 Subject: [PATCH 23/38] Review config flags #1784 --- src/textures.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/textures.c b/src/textures.c index f46b0c9a6..dbad62203 100644 --- a/src/textures.c +++ b/src/textures.c @@ -67,6 +67,7 @@ #include // Required for: malloc(), free() #include // Required for: strlen() [Used in ImageTextEx()] #include // Required for: fabsf() +#include // Required for: sprintf() [Used in ExportImageAsCode()] #include "utils.h" // Required for: fopen() Android mapping @@ -122,7 +123,7 @@ // NOTE: Used to read image data (multiple formats support) #endif -#if (defined(SUPPORT_IMAGE_EXPORT) || defined(SUPPORT_COMPRESSION_API)) +#if defined(SUPPORT_IMAGE_EXPORT) #define STBIW_MALLOC RL_MALLOC #define STBIW_FREE RL_FREE #define STBIW_REALLOC RL_REALLOC @@ -459,6 +460,8 @@ bool ExportImageAsCode(Image image, const char *fileName) { bool success = false; +#if defined(SUPPORT_IMAGE_EXPORT) + #ifndef TEXT_BYTES_PER_LINE #define TEXT_BYTES_PER_LINE 20 #endif @@ -500,6 +503,11 @@ bool ExportImageAsCode(Image image, const char *fileName) success = SaveFileText(fileName, txtData); RL_FREE(txtData); + +#endif // SUPPORT_IMAGE_EXPORT + + if (success != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Image exported successfully", fileName); + else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export image", fileName); return success; } From 7bcb2ad4f10c7b8508c706e538994830b012154b Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 31 May 2021 11:39:20 +0200 Subject: [PATCH 24/38] Update uwp_events.h --- src/uwp_events.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uwp_events.h b/src/uwp_events.h index 2c403fd36..09f7a7f7f 100644 --- a/src/uwp_events.h +++ b/src/uwp_events.h @@ -5,7 +5,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2020-2020 Reece Mackie (@Rover656) +* Copyright (c) 2020-2021 Reece Mackie (@Rover656) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. From 1c5de9721a8c9b9543044b461d4a9480c1d25b43 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 31 May 2021 11:41:56 +0200 Subject: [PATCH 25/38] WARNING: BREAKING: RENAMED: RayHitInfo to RayCollision #1781 - RENAMED: RayHitInfo to RayCollision - RENAMED/REDESIGNED: Multiple Ray collision functions to be more consistent and return RayCollision data -WIP- --- examples/models/models_mesh_picking.c | 62 +++++++-------- src/models.c | 107 +++++++++++--------------- src/raylib.h | 19 +++-- 3 files changed, 85 insertions(+), 103 deletions(-) diff --git a/examples/models/models_mesh_picking.c b/examples/models/models_mesh_picking.c index 5dda8df5c..31500bce1 100644 --- a/examples/models/models_mesh_picking.c +++ b/examples/models/models_mesh_picking.c @@ -63,53 +63,53 @@ int main(void) UpdateCamera(&camera); // Update camera // Display information about closest hit - RayHitInfo nearestHit = { 0 }; + RayCollision collision = { 0 }; char *hitObjectName = "None"; - nearestHit.distance = FLT_MAX; - nearestHit.hit = false; + collision.distance = FLT_MAX; + collision.hit = false; Color cursorColor = WHITE; // Get ray and test against ground, triangle, and mesh ray = GetMouseRay(GetMousePosition(), camera); // Check ray collision aginst ground plane - RayHitInfo groundHitInfo = GetCollisionRayGround(ray, 0.0f); + RayCollision groundHitInfo = GetRayCollisionGround(ray, 0.0f); - if ((groundHitInfo.hit) && (groundHitInfo.distance < nearestHit.distance)) + if ((groundHitInfo.hit) && (groundHitInfo.distance < collision.distance)) { - nearestHit = groundHitInfo; + collision = groundHitInfo; cursorColor = GREEN; hitObjectName = "Ground"; } // Check ray collision against test triangle - RayHitInfo triHitInfo = GetCollisionRayTriangle(ray, ta, tb, tc); + RayCollision triHitInfo = GetRayCollisionTriangle(ray, ta, tb, tc); - if ((triHitInfo.hit) && (triHitInfo.distance < nearestHit.distance)) + if ((triHitInfo.hit) && (triHitInfo.distance < collision.distance)) { - nearestHit = triHitInfo; + collision = triHitInfo; cursorColor = PURPLE; hitObjectName = "Triangle"; - bary = Vector3Barycenter(nearestHit.position, ta, tb, tc); + bary = Vector3Barycenter(collision.point, ta, tb, tc); hitTriangle = true; } else hitTriangle = false; - RayHitInfo meshHitInfo = { 0 }; + RayCollision meshHitInfo = { 0 }; // Check ray collision against bounding box first, before trying the full ray-mesh test - if (CheckCollisionRayBox(ray, towerBBox)) + if (GetRayCollisionBox(ray, towerBBox).hit) { hitMeshBBox = true; // Check ray collision against model // NOTE: It considers model.transform matrix! - meshHitInfo = GetCollisionRayModel(ray, tower); + meshHitInfo = GetRayCollisionModel(ray, tower); - if ((meshHitInfo.hit) && (meshHitInfo.distance < nearestHit.distance)) + if ((meshHitInfo.hit) && (meshHitInfo.distance < collision.distance)) { - nearestHit = meshHitInfo; + collision = meshHitInfo; cursorColor = ORANGE; hitObjectName = "Mesh"; } @@ -128,7 +128,7 @@ int main(void) // Draw the tower // WARNING: If scale is different than 1.0f, - // not considered by GetCollisionRayModel() + // not considered by GetRayCollisionModel() DrawModel(tower, towerPos, 1.0f, WHITE); // Draw the test triangle @@ -140,17 +140,17 @@ int main(void) if (hitMeshBBox) DrawBoundingBox(towerBBox, LIME); // If we hit something, draw the cursor at the hit point - if (nearestHit.hit) + if (collision.hit) { - DrawCube(nearestHit.position, 0.3f, 0.3f, 0.3f, cursorColor); - DrawCubeWires(nearestHit.position, 0.3f, 0.3f, 0.3f, RED); + DrawCube(collision.point, 0.3f, 0.3f, 0.3f, cursorColor); + DrawCubeWires(collision.point, 0.3f, 0.3f, 0.3f, RED); Vector3 normalEnd; - normalEnd.x = nearestHit.position.x + nearestHit.normal.x; - normalEnd.y = nearestHit.position.y + nearestHit.normal.y; - normalEnd.z = nearestHit.position.z + nearestHit.normal.z; + normalEnd.x = collision.point.x + collision.normal.x; + normalEnd.y = collision.point.y + collision.normal.y; + normalEnd.z = collision.point.z + collision.normal.z; - DrawLine3D(nearestHit.position, normalEnd, RED); + DrawLine3D(collision.point, normalEnd, RED); } DrawRay(ray, MAROON); @@ -162,21 +162,21 @@ int main(void) // Draw some debug GUI text DrawText(TextFormat("Hit Object: %s", hitObjectName), 10, 50, 10, BLACK); - if (nearestHit.hit) + if (collision.hit) { int ypos = 70; - DrawText(TextFormat("Distance: %3.2f", nearestHit.distance), 10, ypos, 10, BLACK); + DrawText(TextFormat("Distance: %3.2f", collision.distance), 10, ypos, 10, BLACK); DrawText(TextFormat("Hit Pos: %3.2f %3.2f %3.2f", - nearestHit.position.x, - nearestHit.position.y, - nearestHit.position.z), 10, ypos + 15, 10, BLACK); + collision.point.x, + collision.point.y, + collision.point.z), 10, ypos + 15, 10, BLACK); DrawText(TextFormat("Hit Norm: %3.2f %3.2f %3.2f", - nearestHit.normal.x, - nearestHit.normal.y, - nearestHit.normal.z), 10, ypos + 30, 10, BLACK); + collision.normal.x, + collision.normal.y, + collision.normal.z), 10, ypos + 30, 10, BLACK); if (hitTriangle) DrawText(TextFormat("Barycenter: %3.2f %3.2f %3.2f", bary.x, bary.y, bary.z), 10, ypos + 45, 10, BLACK); } diff --git a/src/models.c b/src/models.c index 101677ca5..9217cc2db 100644 --- a/src/models.c +++ b/src/models.c @@ -2979,53 +2979,32 @@ bool CheckCollisionBoxSphere(BoundingBox box, Vector3 center, float radius) return collision; } -// Detect collision between ray and sphere -bool CheckCollisionRaySphere(Ray ray, Vector3 center, float radius) +// Get collision info between ray and sphere +RayCollision GetRayCollisionSphere(Ray ray, Vector3 center, float radius) { - bool collision = false; + RayCollision collision = { 0 }; Vector3 raySpherePos = Vector3Subtract(center, ray.position); float distance = Vector3Length(raySpherePos); float vector = Vector3DotProduct(raySpherePos, ray.direction); float d = radius*radius - (distance*distance - vector*vector); - if (d >= 0.0f) collision = true; - - return collision; -} - -// Detect collision between ray and sphere with extended parameters and collision point detection -bool CheckCollisionRaySphereEx(Ray ray, Vector3 center, float radius, Vector3 *collisionPoint) -{ - bool collision = false; - - Vector3 raySpherePos = Vector3Subtract(center, ray.position); - float distance = Vector3Length(raySpherePos); - float vector = Vector3DotProduct(raySpherePos, ray.direction); - float d = radius*radius - (distance*distance - vector*vector); - - if (d >= 0.0f) collision = true; + if (d >= 0.0f) collision.hit = true; // Check if ray origin is inside the sphere to calculate the correct collision point - float collisionDistance = 0; - - if (distance < radius) collisionDistance = vector + sqrtf(d); - else collisionDistance = vector - sqrtf(d); + if (distance < radius) collision.distance = vector + sqrtf(d); + else collision.distance = vector - sqrtf(d); // Calculate collision point - Vector3 cPoint = Vector3Add(ray.position, Vector3Scale(ray.direction, collisionDistance)); - - collisionPoint->x = cPoint.x; - collisionPoint->y = cPoint.y; - collisionPoint->z = cPoint.z; + collision.point = Vector3Add(ray.position, Vector3Scale(ray.direction, collision.distance)); return collision; } -// Detect collision between ray and bounding box -bool CheckCollisionRayBox(Ray ray, BoundingBox box) +// Get collision info between ray and box +RayCollision GetRayCollisionBox(Ray ray, BoundingBox box) { - bool collision = false; + RayCollision collision = { 0 }; float t[8] = { 0 }; t[0] = (box.min.x - ray.position.x)/ray.direction.x; @@ -3037,14 +3016,17 @@ bool CheckCollisionRayBox(Ray ray, BoundingBox box) t[6] = (float)fmax(fmax(fmin(t[0], t[1]), fmin(t[2], t[3])), fmin(t[4], t[5])); t[7] = (float)fmin(fmin(fmax(t[0], t[1]), fmax(t[2], t[3])), fmax(t[4], t[5])); - collision = !(t[7] < 0 || t[6] > t[7]); + collision.hit = !(t[7] < 0 || t[6] > t[7]); + + // TODO: Calculate other RayCollision data return collision; } + // Get collision info between ray and mesh -RayHitInfo GetCollisionRayMesh(Ray ray, Mesh mesh, Matrix transform) +RayCollision GetRayCollisionMesh(Ray ray, Mesh mesh, Matrix transform) { - RayHitInfo result = { 0 }; + RayCollision collision = { 0 }; // Check if mesh vertex data on CPU for testing if (mesh.vertices != NULL) @@ -3074,47 +3056,49 @@ RayHitInfo GetCollisionRayMesh(Ray ray, Mesh mesh, Matrix transform) b = Vector3Transform(b, transform); c = Vector3Transform(c, transform); - RayHitInfo triHitInfo = GetCollisionRayTriangle(ray, a, b, c); + RayCollision triHitInfo = GetRayCollisionTriangle(ray, a, b, c); if (triHitInfo.hit) { // Save the closest hit triangle - if ((!result.hit) || (result.distance > triHitInfo.distance)) result = triHitInfo; + if ((!collision.hit) || (collision.distance > triHitInfo.distance)) collision = triHitInfo; } } } - return result; + + return collision; } // Get collision info between ray and model -RayHitInfo GetCollisionRayModel(Ray ray, Model model) +RayCollision GetRayCollisionModel(Ray ray, Model model) { - RayHitInfo result = { 0 }; + RayCollision collision = { 0 }; for (int m = 0; m < model.meshCount; m++) { - RayHitInfo meshHitInfo = GetCollisionRayMesh(ray, model.meshes[m], model.transform); + RayCollision meshHitInfo = GetRayCollisionMesh(ray, model.meshes[m], model.transform); if (meshHitInfo.hit) { // Save the closest hit mesh - if ((!result.hit) || (result.distance > meshHitInfo.distance)) result = meshHitInfo; + if ((!collision.hit) || (collision.distance > meshHitInfo.distance)) collision = meshHitInfo; } } - return result; + return collision; } // Get collision info between ray and triangle // NOTE: Based on https://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm -RayHitInfo GetCollisionRayTriangle(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3) +RayCollision GetRayCollisionTriangle(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3) { #define EPSILON 0.000001 // A small number - Vector3 edge1, edge2; + RayCollision collision = { 0 }; + Vector3 edge1 = { 0 }; + Vector3 edge2 = { 0 }; Vector3 p, q, tv; float det, invDet, u, v, t; - RayHitInfo result = {0}; // Find vectors for two edges sharing V1 edge1 = Vector3Subtract(p2, p1); @@ -3127,7 +3111,7 @@ RayHitInfo GetCollisionRayTriangle(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3) det = Vector3DotProduct(edge1, p); // Avoid culling! - if ((det > -EPSILON) && (det < EPSILON)) return result; + if ((det > -EPSILON) && (det < EPSILON)) return collision; invDet = 1.0f/det; @@ -3138,7 +3122,7 @@ RayHitInfo GetCollisionRayTriangle(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3) u = Vector3DotProduct(tv, p)*invDet; // The intersection lies outside of the triangle - if ((u < 0.0f) || (u > 1.0f)) return result; + if ((u < 0.0f) || (u > 1.0f)) return collision; // Prepare to test v parameter q = Vector3CrossProduct(tv, edge1); @@ -3147,29 +3131,28 @@ RayHitInfo GetCollisionRayTriangle(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3) v = Vector3DotProduct(ray.direction, q)*invDet; // The intersection lies outside of the triangle - if ((v < 0.0f) || ((u + v) > 1.0f)) return result; + if ((v < 0.0f) || ((u + v) > 1.0f)) return collision; t = Vector3DotProduct(edge2, q)*invDet; if (t > EPSILON) { // Ray hit, get hit point and normal - result.hit = true; - result.distance = t; - result.hit = true; - result.normal = Vector3Normalize(Vector3CrossProduct(edge1, edge2)); - result.position = Vector3Add(ray.position, Vector3Scale(ray.direction, t)); + collision.hit = true; + collision.distance = t; + collision.normal = Vector3Normalize(Vector3CrossProduct(edge1, edge2)); + collision.point = Vector3Add(ray.position, Vector3Scale(ray.direction, t)); } - return result; + return collision; } // Get collision info between ray and ground plane (Y-normal plane) -RayHitInfo GetCollisionRayGround(Ray ray, float groundHeight) +RayCollision GetRayCollisionGround(Ray ray, float groundHeight) { #define EPSILON 0.000001 // A small number - RayHitInfo result = { 0 }; + RayCollision collision = { 0 }; if (fabsf(ray.direction.y) > EPSILON) { @@ -3177,15 +3160,15 @@ RayHitInfo GetCollisionRayGround(Ray ray, float groundHeight) if (distance >= 0.0) { - result.hit = true; - result.distance = distance; - result.normal = (Vector3){ 0.0, 1.0, 0.0 }; - result.position = Vector3Add(ray.position, Vector3Scale(ray.direction, distance)); - result.position.y = groundHeight; + collision.hit = true; + collision.distance = distance; + collision.normal = (Vector3){ 0.0, 1.0, 0.0 }; + collision.point = Vector3Add(ray.position, Vector3Scale(ray.direction, distance)); + collision.point.y = groundHeight; } } - return result; + return collision; } //---------------------------------------------------------------------------------- diff --git a/src/raylib.h b/src/raylib.h index 63d1bdf62..5f57496c1 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -399,12 +399,12 @@ typedef struct Ray { } Ray; // RayCollision, ray hit information -typedef struct RayHitInfo { +typedef struct RayCollision { bool hit; // Did the ray hit something? float distance; // Distance to nearest hit - Vector3 position; // Position of nearest hit + Vector3 point; // Point of nearest hit Vector3 normal; // Surface normal of hit -} RayHitInfo; +} RayCollision; // BoundingBox typedef struct BoundingBox { @@ -1445,13 +1445,12 @@ RLAPI void DrawBillboardPro(Camera camera, Texture2D texture, Rectangle source, RLAPI bool CheckCollisionSpheres(Vector3 center1, float radius1, Vector3 center2, float radius2); // Detect collision between two spheres RLAPI bool CheckCollisionBoxes(BoundingBox box1, BoundingBox box2); // Detect collision between two bounding boxes RLAPI bool CheckCollisionBoxSphere(BoundingBox box, Vector3 center, float radius); // Detect collision between box and sphere -RLAPI bool CheckCollisionRaySphere(Ray ray, Vector3 center, float radius); // Detect collision between ray and sphere -RLAPI bool CheckCollisionRaySphereEx(Ray ray, Vector3 center, float radius, Vector3 *collisionPoint); // Detect collision between ray and sphere, returns collision point -RLAPI bool CheckCollisionRayBox(Ray ray, BoundingBox box); // Detect collision between ray and box -RLAPI RayHitInfo GetCollisionRayMesh(Ray ray, Mesh mesh, Matrix transform); // Get collision info between ray and mesh -RLAPI RayHitInfo GetCollisionRayModel(Ray ray, Model model); // Get collision info between ray and model -RLAPI RayHitInfo GetCollisionRayTriangle(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3); // Get collision info between ray and triangle -RLAPI RayHitInfo GetCollisionRayGround(Ray ray, float groundHeight); // Get collision info between ray and ground plane (Y-normal plane) +RLAPI RayCollision GetRayCollisionSphere(Ray ray, Vector3 center, float radius); // Get collision info between ray and sphere +RLAPI RayCollision GetRayCollisionBox(Ray ray, BoundingBox box); // Get collision info between ray and box +RLAPI RayCollision GetRayCollisionMesh(Ray ray, Mesh mesh, Matrix transform); // Get collision info between ray and mesh +RLAPI RayCollision GetRayCollisionModel(Ray ray, Model model); // Get collision info between ray and model +RLAPI RayCollision GetRayCollisionTriangle(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3); // Get collision info between ray and triangle +RLAPI RayCollision GetRayCollisionGround(Ray ray, float groundHeight); // Get collision info between ray and ground plane (Y-normal plane) //------------------------------------------------------------------------------------ // Audio Loading and Playing Functions (Module: audio) From 9bd3d78374782b45d0d7f1a41f2b545d631dab33 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 31 May 2021 11:43:56 +0200 Subject: [PATCH 26/38] WARNING: Moved some **extra** raylib libraries to /extras/ directory --- src/{ => extras}/easings.h | 0 src/{ => extras}/physac.h | 0 src/{ => extras}/rmem.h | 0 src/{ => extras}/rnet.h | 4510 ++++++++++++++++++------------------ 4 files changed, 2255 insertions(+), 2255 deletions(-) rename src/{ => extras}/easings.h (100%) rename src/{ => extras}/physac.h (100%) rename src/{ => extras}/rmem.h (100%) rename src/{ => extras}/rnet.h (97%) diff --git a/src/easings.h b/src/extras/easings.h similarity index 100% rename from src/easings.h rename to src/extras/easings.h diff --git a/src/physac.h b/src/extras/physac.h similarity index 100% rename from src/physac.h rename to src/extras/physac.h diff --git a/src/rmem.h b/src/extras/rmem.h similarity index 100% rename from src/rmem.h rename to src/extras/rmem.h diff --git a/src/rnet.h b/src/extras/rnet.h similarity index 97% rename from src/rnet.h rename to src/extras/rnet.h index 7ed29b15b..439b105a2 100644 --- a/src/rnet.h +++ b/src/extras/rnet.h @@ -1,2256 +1,2256 @@ -/********************************************************************************************** -* -* rnet - A simple and easy-to-use network module for raylib -* -* FEATURES: -* - Provides a simple and (hopefully) easy to use wrapper around the Berkeley socket API -* -* INSPIRED BY: -* SFML Sockets - https://www.sfml-dev.org/documentation/2.5.1/classsf_1_1Socket.php -* SDL_net - https://www.libsdl.org/projects/SDL_net/ -* BSD Sockets - https://www.gnu.org/software/libc/manual/html_node/Sockets.html -* BEEJ - https://beej.us/guide/bgnet/html/single/bgnet.html -* Winsock2 - https://docs.microsoft.com/en-us/windows/desktop/api/winsock2 -* -* CONTRIBUTORS: -* Jak Barnes (github: @syphonx) (Feb. 2019) - Initial version -* -* -* LICENSE: zlib/libpng -* -* Copyright (c) 2019-2020 Jak Barnes (@syphonx) and Ramon Santamaria (@raysan5) -* -* This software is provided "as-is", without any express or implied warranty. In no event -* will the authors be held liable for any damages arising from the use of this software. -* -* Permission is granted to anyone to use this software for any purpose, including commercial -* applications, and to alter it and redistribute it freely, subject to the following restrictions: -* -* 1. The origin of this software must not be misrepresented; you must not claim that you -* wrote the original software. If you use this software in a product, an acknowledgment -* in the product documentation would be appreciated but is not required. -* -* 2. Altered source versions must be plainly marked as such, and must not be misrepresented -* as being the original software. -* -* 3. This notice may not be removed or altered from any source distribution. -* -**********************************************************************************************/ - -#ifndef RNET_H -#define RNET_H - -#include // Required for limits -#include // Required for platform type sizes - -//---------------------------------------------------------------------------------- -// Defines and Macros -//---------------------------------------------------------------------------------- - -// Undefine any conflicting windows.h symbols -// If defined, the following flags inhibit definition of the indicated items. -#define NOGDICAPMASKS // CC_*, LC_*, PC_*, CP_*, TC_*, RC_ -#define NOVIRTUALKEYCODES // VK_* -#define NOWINMESSAGES // WM_*, EM_*, LB_*, CB_* -#define NOWINSTYLES // WS_*, CS_*, ES_*, LBS_*, SBS_*, CBS_* -#define NOSYSMETRICS // SM_* -#define NOMENUS // MF_* -#define NOICONS // IDI_* -#define NOKEYSTATES // MK_* -#define NOSYSCOMMANDS // SC_* -#define NORASTEROPS // Binary and Tertiary raster ops -#define NOSHOWWINDOW // SW_* -#define OEMRESOURCE // OEM Resource values -#define NOATOM // Atom Manager routines -#define NOCLIPBOARD // Clipboard routines -#define NOCOLOR // Screen colors -#define NOCTLMGR // Control and Dialog routines -#define NODRAWTEXT // DrawText() and DT_* -#define NOGDI // All GDI defines and routines -#define NOKERNEL // All KERNEL defines and routines -#define NOUSER // All USER defines and routines -#define NONLS // All NLS defines and routines -#define NOMB // MB_* and MessageBox() -#define NOMEMMGR // GMEM_*, LMEM_*, GHND, LHND, associated routines -#define NOMETAFILE // typedef METAFILEPICT -#define NOMINMAX // Macros min(a,b) and max(a,b) -#define NOMSG // typedef MSG and associated routines -#define NOOPENFILE // OpenFile(), OemToAnsi, AnsiToOem, and OF_* -#define NOSCROLL // SB_* and scrolling routines -#define NOSERVICE // All Service Controller routines, SERVICE_ equates, etc. -#define NOSOUND // Sound driver routines -#define NOTEXTMETRIC // typedef TEXTMETRIC and associated routines -#define NOWH // SetWindowsHook and WH_* -#define NOWINOFFSETS // GWL_*, GCL_*, associated routines -#define NOCOMM // COMM driver routines -#define NOKANJI // Kanji support stuff. -#define NOHELP // Help engine interface. -#define NOPROFILER // Profiler interface. -#define NODEFERWINDOWPOS // DeferWindowPos routines -#define NOMCX // Modem Configuration Extensions -#define MMNOSOUND - -// Allow custom memory allocators -#ifndef RNET_MALLOC - #define RNET_MALLOC(sz) malloc(sz) -#endif -#ifndef RNET_CALLOC - #define RNET_CALLOC(n,sz) calloc(n,sz) -#endif -#ifndef RNET_FREE - #define RNET_FREE(p) free(p) -#endif - -//---------------------------------------------------------------------------------- -// Platform type definitions -// From: https://github.com/DFHack/clsocket/blob/master/src/Host.h -//---------------------------------------------------------------------------------- - -#ifdef WIN32 -typedef int socklen_t; -#endif - -#ifndef RESULT_SUCCESS -# define RESULT_SUCCESS 0 -#endif // RESULT_SUCCESS - -#ifndef RESULT_FAILURE -# define RESULT_FAILURE 1 -#endif // RESULT_FAILURE - -#ifndef htonll -# ifdef _BIG_ENDIAN -# define htonll(x) (x) -# define ntohll(x) (x) -# else -# define htonll(x) ((((uint64) htonl(x)) << 32) + htonl(x >> 32)) -# define ntohll(x) ((((uint64) ntohl(x)) << 32) + ntohl(x >> 32)) -# endif // _BIG_ENDIAN -#endif // htonll - -//---------------------------------------------------------------------------------- -// Platform specific network includes -// From: https://github.com/SDL-mirror/SDL_net/blob/master/SDLnetsys.h -//---------------------------------------------------------------------------------- - -// Include system network headers -#if defined(_WIN32) // Windows - #define __USE_W32_SOCKETS - #define WIN32_LEAN_AND_MEAN - #include - #include - #include - #define IPTOS_LOWDELAY 0x10 -#else // Unix - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include -#endif - -#ifndef INVALID_SOCKET - #define INVALID_SOCKET ~(0) -#endif - -#ifndef __USE_W32_SOCKETS - #define closesocket close - #define SOCKET int - #define INVALID_SOCKET -1 - #define SOCKET_ERROR -1 -#endif - -#ifdef __USE_W32_SOCKETS - #ifndef EINTR - #define EINTR WSAEINTR - #endif -#endif - -//---------------------------------------------------------------------------------- -// Module defines -//---------------------------------------------------------------------------------- - -// Network connection related defines -#define SOCKET_MAX_SET_SIZE 32 // Maximum sockets in a set -#define SOCKET_MAX_QUEUE_SIZE 16 // Maximum socket queue size -#define SOCKET_MAX_SOCK_OPTS 4 // Maximum socket options -#define SOCKET_MAX_UDPCHANNELS 32 // Maximum UDP channels -#define SOCKET_MAX_UDPADDRESSES 4 // Maximum bound UDP addresses - -// Network address related defines -#define ADDRESS_IPV4_ADDRSTRLEN 22 // IPv4 string length -#define ADDRESS_IPV6_ADDRSTRLEN 65 // IPv6 string length -#define ADDRESS_TYPE_ANY 0 // AF_UNSPEC -#define ADDRESS_TYPE_IPV4 2 // AF_INET -#define ADDRESS_TYPE_IPV6 23 // AF_INET6 -#define ADDRESS_MAXHOST 1025 // Max size of a fully-qualified domain name -#define ADDRESS_MAXSERV 32 // Max size of a service name - -// Network address related defines -#define ADDRESS_ANY (unsigned long)0x00000000 -#define ADDRESS_LOOPBACK 0x7f000001 -#define ADDRESS_BROADCAST (unsigned long)0xffffffff -#define ADDRESS_NONE 0xffffffff - -// Network resolution related defines -#define NAME_INFO_DEFAULT 0x00 // No flags set -#define NAME_INFO_NOFQDN 0x01 // Only return nodename portion for local hosts -#define NAME_INFO_NUMERICHOST 0x02 // Return numeric form of the host's address -#define NAME_INFO_NAMEREQD 0x04 // Error if the host's name not in DNS -#define NAME_INFO_NUMERICSERV 0x08 // Return numeric form of the service (port #) -#define NAME_INFO_DGRAM 0x10 // Service is a datagram service - -// Address resolution related defines -#if defined(_WIN32) - #define ADDRESS_INFO_PASSIVE (0x00000001) // Socket address will be used in bind() call - #define ADDRESS_INFO_CANONNAME (0x00000002) // Return canonical name in first ai_canonname - #define ADDRESS_INFO_NUMERICHOST (0x00000004) // Nodename must be a numeric address string - #define ADDRESS_INFO_NUMERICSERV (0x00000008) // Servicename must be a numeric port number - #define ADDRESS_INFO_DNS_ONLY (0x00000010) // Restrict queries to unicast DNS only (no LLMNR, netbios, etc.) - #define ADDRESS_INFO_ALL (0x00000100) // Query both IP6 and IP4 with AI_V4MAPPED - #define ADDRESS_INFO_ADDRCONFIG (0x00000400) // Resolution only if global address configured - #define ADDRESS_INFO_V4MAPPED (0x00000800) // On v6 failure, query v4 and convert to V4MAPPED format - #define ADDRESS_INFO_NON_AUTHORITATIVE (0x00004000) // LUP_NON_AUTHORITATIVE - #define ADDRESS_INFO_SECURE (0x00008000) // LUP_SECURE - #define ADDRESS_INFO_RETURN_PREFERRED_NAMES (0x00010000) // LUP_RETURN_PREFERRED_NAMES - #define ADDRESS_INFO_FQDN (0x00020000) // Return the FQDN in ai_canonname - #define ADDRESS_INFO_FILESERVER (0x00040000) // Resolving fileserver name resolution - #define ADDRESS_INFO_DISABLE_IDN_ENCODING (0x00080000) // Disable Internationalized Domain Names handling - #define ADDRESS_INFO_EXTENDED (0x80000000) // Indicates this is extended ADDRINFOEX(2/..) struct - #define ADDRESS_INFO_RESOLUTION_HANDLE (0x40000000) // Request resolution handle -#endif - -//---------------------------------------------------------------------------------- -// Types and Structures Definition -//---------------------------------------------------------------------------------- - -// Boolean type -#ifdef _WIN32 - #include -#else -#if defined(__STDC__) && __STDC_VERSION__ >= 199901L - #include -#elif !defined(__cplusplus) && !defined(bool) - typedef enum { false, true } bool; -#endif -#endif - -typedef enum { - SOCKET_TCP = 0, // SOCK_STREAM - SOCKET_UDP = 1 // SOCK_DGRAM -} SocketType; - -// Network typedefs -typedef uint32_t SocketChannel; -typedef struct _AddressInformation *AddressInformation; -typedef struct _SocketAddress *SocketAddress; -typedef struct _SocketAddressIPv4 *SocketAddressIPv4; -typedef struct _SocketAddressIPv6 *SocketAddressIPv6; -typedef struct _SocketAddressStorage *SocketAddressStorage; - -// IPAddress definition (in network byte order) -typedef struct IPAddress { - unsigned long host; // 32-bit IPv4 host address - unsigned short port; // 16-bit protocol port -} IPAddress; - -typedef struct UDPChannel { - int numbound; // The total number of addresses this channel is bound to - IPAddress address[SOCKET_MAX_UDPADDRESSES]; // The list of remote addresses this channel is bound to -} UDPChannel; - -// An option ID, value, sizeof(value) tuple for setsockopt(2). -typedef struct SocketOpt { - int id; // Socked option id - int valueLen; // Socked option value len - void *value; // Socked option value data -} SocketOpt; - -typedef struct Socket { - int ready; // Is the socket ready? i.e. has information - int status; // The last status code to have occured using this socket - bool isServer; // Is this socket a server socket (i.e. TCP/UDP Listen Server) - SocketChannel channel; // The socket handle id - SocketType type; // Is this socket a TCP or UDP socket? - - bool isIPv6; // Is this socket address an ipv6 address? - SocketAddressIPv4 addripv4; // The host/target IPv4 for this socket (in network byte order) - SocketAddressIPv6 addripv6; // The host/target IPv6 for this socket (in network byte order) - - struct UDPChannel binding[SOCKET_MAX_UDPCHANNELS]; // The amount of channels (if UDP) this socket is bound to -} Socket; - -// Configuration for a socket -typedef struct SocketConfig { - SocketType type; // The type of socket, TCP/UDP - char *host; // The host address in xxx.xxx.xxx.xxx form - char *port; // The target port/service in the form "http" or "25565" - bool server; // Listen for incoming clients? - bool nonblocking; // non-blocking operation? - int backlog_size; // set a custom backlog size - SocketOpt sockopts[SOCKET_MAX_SOCK_OPTS]; -} SocketConfig; - -typedef struct SocketDataPacket { - IPAddress address; // The source/dest address of an incoming/outgoing packet - int channel; // The src/dst channel of the packet - int maxlen; // The size of the data buffer - int status; // Packet status after sending - unsigned int len; // The length of the packet data - unsigned char *data; // The packet data -} SocketDataPacket; - -// Result from calling open with a given config -typedef struct SocketResult { - int status; // Socket result state - Socket *socket; // Socket ref -} SocketResult; - -typedef struct SocketSet { - int numsockets; // Socket set count - int maxsockets; // Socket set max - struct Socket **sockets; // Sockets array -} SocketSet; - -// Packet type -typedef struct Packet { - uint32_t size; // The total size of bytes in data - uint32_t offs; // The offset to data access - uint32_t maxs; // The max size of data - uint8_t *data; // Data stored in network byte order -} Packet; - - -#ifdef __cplusplus -extern "C" { // Prevents name mangling of functions -#endif - -//---------------------------------------------------------------------------------- -// Global Variables Definition -//---------------------------------------------------------------------------------- -//... - -//---------------------------------------------------------------------------------- -// Module Functions Declaration -//---------------------------------------------------------------------------------- - -// Initialisation and cleanup -bool InitNetworkDevice(void); -void CloseNetworkDevice(void); - -// Address API -void ResolveIP(const char *ip, const char *service, int flags, char *outhost, char *outserv); -int ResolveHost(const char *address, const char *service, int addressType, int flags, AddressInformation *outAddr); -int GetAddressFamily(AddressInformation address); -int GetAddressSocketType(AddressInformation address); -int GetAddressProtocol(AddressInformation address); -char *GetAddressCanonName(AddressInformation address); -char *GetAddressHostAndPort(AddressInformation address, char *outhost, unsigned short *outport); - -// Address Memory API -AddressInformation LoadAddress(void); -void UnloadAddress(AddressInformation *addressInfo); -AddressInformation *LoadAddressList(int size); - -// Socket API -bool SocketCreate(SocketConfig *config, SocketResult *result); -bool SocketBind(SocketConfig *config, SocketResult *result); -bool SocketListen(SocketConfig *config, SocketResult *result); -bool SocketConnect(SocketConfig *config, SocketResult *result); -Socket *SocketAccept(Socket *server, SocketConfig *config); - -// General Socket API -int SocketSend(Socket *sock, const void *datap, int len); -int SocketReceive(Socket *sock, void *data, int maxlen); -SocketAddressStorage SocketGetPeerAddress(Socket *sock); -const char *GetSocketAddressHost(SocketAddressStorage storage); -short GetSocketAddressPort(SocketAddressStorage storage); -void SocketClose(Socket *sock); - -// UDP Socket API -int SocketSetChannel(Socket *socket, int channel, const IPAddress *address); -void SocketUnsetChannel(Socket *socket, int channel); - -// UDP DataPacket API -SocketDataPacket *AllocPacket(int size); -int ResizePacket(SocketDataPacket *packet, int newsize); -void FreePacket(SocketDataPacket *packet); -SocketDataPacket **AllocPacketList(int count, int size); -void FreePacketList(SocketDataPacket **packets); - -// Socket Memory API -Socket *LoadSocket(void); -void UnloadSocket(Socket **sock); -SocketResult *LoadSocketResult(void); -void UnloadSocketResult(SocketResult **result); -SocketSet *LoadSocketSet(int max); -void UnloadSocketSet(SocketSet *sockset); - -// Socket I/O API -bool IsSocketReady(Socket *sock); -bool IsSocketConnected(Socket *sock); -int AddSocket(SocketSet *set, Socket *sock); -int RemoveSocket(SocketSet *set, Socket *sock); -int CheckSockets(SocketSet *set, unsigned int timeout); - -// Packet API -void PacketSend(Packet *packet); -void PacketReceive(Packet *packet); -void PacketWrite8(Packet *packet, uint16_t value); -void PacketWrite16(Packet *packet, uint16_t value); -void PacketWrite32(Packet *packet, uint32_t value); -void PacketWrite64(Packet *packet, uint64_t value); -uint16_t PacketRead8(Packet *packet); -uint16_t PacketRead16(Packet *packet); -uint32_t PacketRead32(Packet *packet); -uint64_t PacketRead64(Packet *packet); - -#ifdef __cplusplus -} -#endif - -#endif // RNET_H - -/*********************************************************************************** -* -* RNET IMPLEMENTATION -* -************************************************************************************/ - -#if defined(RNET_IMPLEMENTATION) - -#include // Required for: assert() -#include // Required for: FILE, fopen(), fclose(), fread() -#include // Required for: malloc(), free() -#include // Required for: strcmp(), strncmp() - -#define NET_DEBUG_ENABLED 1 - -#if defined(SUPPORT_TRACELOG) - #define TRACELOG(level, ...) TraceLog(level, __VA_ARGS__) - - #if defined(SUPPORT_TRACELOG_DEBUG) - #define TRACELOGD(...) TraceLog(LOG_DEBUG, __VA_ARGS__) - #else - #define TRACELOGD(...) (void)0 - #endif -#else - #define TRACELOG(level, ...) (void)0 - #define TRACELOGD(...) (void)0 -#endif - -//---------------------------------------------------------------------------------- -// Types and Structures Definition -//---------------------------------------------------------------------------------- - -typedef struct _SocketAddress -{ - struct sockaddr address; -} _SocketAddress; - -typedef struct _SocketAddressIPv4 -{ - struct sockaddr_in address; -} _SocketAddressIPv4; - -typedef struct _SocketAddressIPv6 -{ - struct sockaddr_in6 address; -} _SocketAddressIPv6; - -typedef struct _SocketAddressStorage -{ - struct sockaddr_storage address; -} _SocketAddressStorage; - -typedef struct _AddressInformation -{ - struct addrinfo addr; -} _AddressInformation; - -//---------------------------------------------------------------------------------- -// Local module Functions Declarations -//---------------------------------------------------------------------------------- -static void PrintSocket(struct sockaddr_storage *addr, const int family, const int socktype, const int protocol); -static const char *SocketAddressToString(struct sockaddr_storage *sockaddr); -static bool IsIPv4Address(const char *ip); -static bool IsIPv6Address(const char *ip); -static void *GetSocketPortPtr(struct sockaddr_storage *sa); -static void *GetSocketAddressPtr(struct sockaddr_storage *sa); -static bool IsSocketValid(Socket *sock); -static void SocketSetLastError(int err); -static int SocketGetLastError(); -static char *SocketGetLastErrorString(); -static char *SocketErrorCodeToString(int err); -static bool SocketSetDefaults(SocketConfig *config); -static bool InitSocket(Socket *sock, struct addrinfo *addr); -static bool CreateSocket(SocketConfig *config, SocketResult *outresult); -static bool SocketSetBlocking(Socket *sock); -static bool SocketSetNonBlocking(Socket *sock); -static bool SocketSetOptions(SocketConfig *config, Socket *sock); -static void SocketSetHints(SocketConfig *config, struct addrinfo *hints); - -//---------------------------------------------------------------------------------- -// Local module Functions Definition -//---------------------------------------------------------------------------------- -// Print socket information -static void PrintSocket(struct sockaddr_storage *addr, const int family, const int socktype, const int protocol) -{ - switch (family) - { - case AF_UNSPEC: TRACELOG(LOG_DEBUG, "\tFamily: Unspecified"); break; - case AF_INET: - { - TRACELOG(LOG_DEBUG, "\tFamily: AF_INET (IPv4)"); - TRACELOG(LOG_INFO, "\t- IPv4 address %s", SocketAddressToString(addr)); - } break; - case AF_INET6: - { - TRACELOG(LOG_DEBUG, "\tFamily: AF_INET6 (IPv6)"); - TRACELOG(LOG_INFO, "\t- IPv6 address %s", SocketAddressToString(addr)); - } break; - case AF_NETBIOS: - { - TRACELOG(LOG_DEBUG, "\tFamily: AF_NETBIOS (NetBIOS)"); - } break; - default: TRACELOG(LOG_DEBUG, "\tFamily: Other %ld", family); break; - } - - TRACELOG(LOG_DEBUG, "\tSocket type:"); - switch (socktype) - { - case 0: TRACELOG(LOG_DEBUG, "\t- Unspecified"); break; - case SOCK_STREAM: TRACELOG(LOG_DEBUG, "\t- SOCK_STREAM (stream)"); break; - case SOCK_DGRAM: TRACELOG(LOG_DEBUG, "\t- SOCK_DGRAM (datagram)"); break; - case SOCK_RAW: TRACELOG(LOG_DEBUG, "\t- SOCK_RAW (raw)"); break; - case SOCK_RDM: TRACELOG(LOG_DEBUG, "\t- SOCK_RDM (reliable message datagram)"); break; - case SOCK_SEQPACKET: TRACELOG(LOG_DEBUG, "\t- SOCK_SEQPACKET (pseudo-stream packet)"); break; - default: TRACELOG(LOG_DEBUG, "\t- Other %ld", socktype); break; - } - - TRACELOG(LOG_DEBUG, "\tProtocol:"); - switch (protocol) - { - case 0: TRACELOG(LOG_DEBUG, "\t- Unspecified"); break; - case IPPROTO_TCP: TRACELOG(LOG_DEBUG, "\t- IPPROTO_TCP (TCP)"); break; - case IPPROTO_UDP: TRACELOG(LOG_DEBUG, "\t- IPPROTO_UDP (UDP)"); break; - default: TRACELOG(LOG_DEBUG, "\t- Other %ld", protocol); break; - } -} - -// Convert network ordered socket address to human readable string (127.0.0.1) -static const char *SocketAddressToString(struct sockaddr_storage *sockaddr) -{ - //static const char* ipv6[INET6_ADDRSTRLEN]; - assert(sockaddr != NULL); - assert(sockaddr->ss_family == AF_INET || sockaddr->ss_family == AF_INET6); - - switch (sockaddr->ss_family) - { - case AF_INET: - { - //struct sockaddr_in *s = ((struct sockaddr_in *)sockaddr); - //return inet_ntop(AF_INET, &s->sin_addr, ipv6, INET_ADDRSTRLEN); // TODO. - } - break; - case AF_INET6: - { - //struct sockaddr_in6 *s = ((struct sockaddr_in6 *)sockaddr); - //return inet_ntop(AF_INET6, &s->sin6_addr, ipv6, INET6_ADDRSTRLEN); // TODO. - } - break; - } - - return NULL; -} - -// Check if the null terminated string ip is a valid IPv4 address -static bool IsIPv4Address(const char *ip) -{ - /* - struct sockaddr_in sa; - int result = inet_pton(AF_INET, ip, &(sa.sin_addr)); // TODO. - return (result != 0); - */ - return false; -} - -// Check if the null terminated string ip is a valid IPv6 address -static bool IsIPv6Address(const char *ip) -{ - /* - struct sockaddr_in6 sa; - int result = inet_pton(AF_INET6, ip, &(sa.sin6_addr)); // TODO. - return result != 0; - */ - return false; -} - -// Return a pointer to the port from the correct address family (IPv4, or IPv6) -static void *GetSocketPortPtr(struct sockaddr_storage *sa) -{ - if (sa->ss_family == AF_INET) - { - return &(((struct sockaddr_in *)sa)->sin_port); - } - - return &(((struct sockaddr_in6 *)sa)->sin6_port); -} - -// Return a pointer to the address from the correct address family (IPv4, or IPv6) -static void *GetSocketAddressPtr(struct sockaddr_storage *sa) -{ - if (sa->ss_family == AF_INET) - { - return &(((struct sockaddr_in *)sa)->sin_addr); - } - - return &(((struct sockaddr_in6 *)sa)->sin6_addr); -} - -// Is the socket in a valid state? -static bool IsSocketValid(Socket *sock) -{ - if (sock != NULL) - { - return (sock->channel != INVALID_SOCKET); - } - - return false; -} - -// Sets the error code that can be retrieved through the WSAGetLastError function. -static void SocketSetLastError(int err) -{ -#if defined(_WIN32) - WSASetLastError(err); -#else - errno = err; -#endif -} - -// Returns the error status for the last Sockets operation that failed -static int SocketGetLastError(void) -{ -#if defined(_WIN32) - return WSAGetLastError(); -#else - return errno; -#endif -} - -// Returns a human-readable string representing the last error message -static char *SocketGetLastErrorString(void) -{ - return SocketErrorCodeToString(SocketGetLastError()); -} - -// Returns a human-readable string representing the error message (err) -static char *SocketErrorCodeToString(int err) -{ -#if defined(_WIN32) - static char gaiStrErrorBuffer[GAI_STRERROR_BUFFER_SIZE]; - TRACELOG(LOG_INFO, gaiStrErrorBuffer, "%s", gai_strerror(err)); - return gaiStrErrorBuffer; -#else - return gai_strerror(err); -#endif -} - -// Set the defaults in the supplied SocketConfig if they're not already set -static bool SocketSetDefaults(SocketConfig *config) -{ - if (config->backlog_size == 0) config->backlog_size = SOCKET_MAX_QUEUE_SIZE; - - return true; -} - -// Create the socket channel -static bool InitSocket(Socket *sckt, struct addrinfo *address) -{ - switch (sckt->type) - { - case SOCKET_TCP: - { - if (address->ai_family == AF_INET) sckt->channel = socket(AF_INET, SOCK_STREAM, 0); - else sckt->channel = socket(AF_INET6, SOCK_STREAM, 0); - } break; - case SOCKET_UDP: - { - if (address->ai_family == AF_INET) sckt->channel = socket(AF_INET, SOCK_DGRAM, 0); - else sckt->channel = socket(AF_INET6, SOCK_DGRAM, 0); - } break; - default: TRACELOG(LOG_WARNING, "Invalid socket type specified."); break; - } - - return IsSocketValid(sckt); -} - -// CreateSocket() - Interally called by CreateSocket() -// -// This here is the bread and butter of the socket API, This function will -// attempt to open a socket, bind and listen to it based on the config passed in -// -// SocketConfig* config - Configuration for which socket to open -// SocketResult* result - The results of this function (if any, including errors) -// -// e.g. -// SocketConfig server_config = { SocketConfig client_config = { -// .host = "127.0.0.1", .host = "127.0.0.1", -// .port = 8080, .port = 8080, -// .server = true, }; -// .nonblocking = true, -// }; -// SocketResult server_res; SocketResult client_res; -static bool CreateSocket(SocketConfig *config, SocketResult *outresult) -{ - bool success = true; - int addrstatus; - struct addrinfo hints; // Address flags (IPV4, IPV6, UDP?) - struct addrinfo *res; // A pointer to the resulting address list - - outresult->socket->channel = INVALID_SOCKET; - outresult->status = RESULT_FAILURE; - - // Set the socket type - outresult->socket->type = config->type; - - // Set the hints based on information in the config - // - // AI_CANONNAME Causes the ai_canonname of the result to the filled out with the host's canonical (real) name. - // AI_PASSIVE: Causes the result's IP address to be filled out with INADDR_ANY (IPv4)or in6addr_any (IPv6); - // Note: This causes a subsequent call to bind() to auto-fill the IP address - // of the struct sockaddr with the address of the current host. - // - SocketSetHints(config, &hints); - - // Populate address information - addrstatus = getaddrinfo(config->host, // e.g. "www.example.com" or IP (Can be null if AI_PASSIVE flag is set - config->port, // e.g. "http" or port number - &hints, // e.g. SOCK_STREAM/SOCK_DGRAM - &res // The struct to populate - ); - - // Did we succeed? - if (addrstatus != 0) - { - outresult->socket->status = SocketGetLastError(); - TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(outresult->socket->status)); - SocketSetLastError(0); - TRACELOG(LOG_WARNING, "Failed to get resolve host %s:%s: %s", config->host, config->port, SocketGetLastErrorString()); - - return (success = false); - } - else - { - char hoststr[NI_MAXHOST]; - char portstr[NI_MAXSERV]; - //socklen_t client_len = sizeof(struct sockaddr_storage); - //int rc = getnameinfo((struct sockaddr *)res->ai_addr, client_len, hoststr, sizeof(hoststr), portstr, sizeof(portstr), NI_NUMERICHOST | NI_NUMERICSERV); - TRACELOG(LOG_INFO, "Successfully resolved host %s:%s", hoststr, portstr); - } - - // Walk the address information linked-list - struct addrinfo *it; - for (it = res; it != NULL; it = it->ai_next) - { - // Initialise the socket - if (!InitSocket(outresult->socket, it)) - { - outresult->socket->status = SocketGetLastError(); - TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(outresult->socket->status)); - SocketSetLastError(0); - continue; - } - - // Set socket options - if (!SocketSetOptions(config, outresult->socket)) - { - outresult->socket->status = SocketGetLastError(); - TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(outresult->socket->status)); - SocketSetLastError(0); - freeaddrinfo(res); - - return (success = false); - } - } - - if (!IsSocketValid(outresult->socket)) - { - outresult->socket->status = SocketGetLastError(); - TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(outresult->status)); - SocketSetLastError(0); - freeaddrinfo(res); - - return (success = false); - } - - if (success) - { - outresult->status = RESULT_SUCCESS; - outresult->socket->ready = 0; - outresult->socket->status = 0; - - if (!(config->type == SOCKET_UDP)) outresult->socket->isServer = config->server; - - switch (res->ai_addr->sa_family) - { - case AF_INET: - { - outresult->socket->addripv4 = (struct _SocketAddressIPv4 *)RNET_MALLOC(sizeof(*outresult->socket->addripv4)); - - if (outresult->socket->addripv4 != NULL) - { - memset(outresult->socket->addripv4, 0, sizeof(*outresult->socket->addripv4)); - - if (outresult->socket->addripv4 != NULL) - { - memcpy(&outresult->socket->addripv4->address, (struct sockaddr_in *)res->ai_addr, sizeof(struct sockaddr_in)); - - outresult->socket->isIPv6 = false; - char hoststr[NI_MAXHOST]; - char portstr[NI_MAXSERV]; - - socklen_t client_len = sizeof(struct sockaddr_storage); - getnameinfo((struct sockaddr *)&outresult->socket->addripv4->address, client_len, hoststr, sizeof(hoststr), portstr, sizeof(portstr), NI_NUMERICHOST | NI_NUMERICSERV); - - TRACELOG(LOG_INFO, "Socket address set to %s:%s", hoststr, portstr); - } - } - } break; - case AF_INET6: - { - outresult->socket->addripv6 = (struct _SocketAddressIPv6 *)RNET_MALLOC( - sizeof(*outresult->socket->addripv6)); - if (outresult->socket->addripv6 != NULL) - { - memset(outresult->socket->addripv6, 0, - sizeof(*outresult->socket->addripv6)); - if (outresult->socket->addripv6 != NULL) - { - memcpy(&outresult->socket->addripv6->address, - (struct sockaddr_in6 *)res->ai_addr, sizeof(struct sockaddr_in6)); - outresult->socket->isIPv6 = true; - char hoststr[NI_MAXHOST]; - char portstr[NI_MAXSERV]; - socklen_t client_len = sizeof(struct sockaddr_storage); - getnameinfo( - (struct sockaddr *)&outresult->socket->addripv6->address, client_len, hoststr, sizeof(hoststr), portstr, sizeof(portstr), NI_NUMERICHOST | NI_NUMERICSERV); - TRACELOG(LOG_INFO, "Socket address set to %s:%s", hoststr, portstr); - } - } - } break; - default: break; - } - } - - freeaddrinfo(res); - return success; -} - -// Set the state of the Socket sock to blocking -static bool SocketSetBlocking(Socket *sock) -{ - bool ret = true; -#if defined(_WIN32) - unsigned long mode = 0; - ret = ioctlsocket(sock->channel, FIONBIO, &mode); -#else - const int flags = fcntl(sock->channel, F_GETFL, 0); - if (!(flags & O_NONBLOCK)) - { - TRACELOG(LOG_DEBUG, "Socket was already in blocking mode"); - return ret; - } - - ret = (0 == fcntl(sock->channel, F_SETFL, (flags ^ O_NONBLOCK))); -#endif - return ret; -} - -// Set the state of the Socket sock to non-blocking -static bool SocketSetNonBlocking(Socket *sock) -{ - bool ret = true; -#if defined(_WIN32) - unsigned long mode = 1; - ret = ioctlsocket(sock->channel, FIONBIO, &mode); -#else - const int flags = fcntl(sock->channel, F_GETFL, 0); - - if ((flags & O_NONBLOCK)) - { - TRACELOG(LOG_DEBUG, "Socket was already in non-blocking mode"); - return ret; - } - - ret = (0 == fcntl(sock->channel, F_SETFL, (flags | O_NONBLOCK))); -#endif - return ret; -} - -// Set options specified in SocketConfig to Socket sock -static bool SocketSetOptions(SocketConfig *config, Socket *sock) -{ - for (int i = 0; i < SOCKET_MAX_SOCK_OPTS; i++) - { - SocketOpt *opt = &config->sockopts[i]; - - if (opt->id == 0) break; - - if (setsockopt(sock->channel, SOL_SOCKET, opt->id, opt->value, opt->valueLen) < 0) return false; - } - - return true; -} - -// Set "hints" in an addrinfo struct, to be passed to getaddrinfo. -static void SocketSetHints(SocketConfig *config, struct addrinfo *hints) -{ - if (config == NULL || hints == NULL) return; - - memset(hints, 0, sizeof(*hints)); - - // Check if the ip supplied in the config is a valid ipv4 ip ipv6 address - if (IsIPv4Address(config->host)) - { - hints->ai_family = AF_INET; - hints->ai_flags |= AI_NUMERICHOST; - } - else - { - if (IsIPv6Address(config->host)) - { - hints->ai_family = AF_INET6; - hints->ai_flags |= AI_NUMERICHOST; - } - else hints->ai_family = AF_UNSPEC; - } - - if (config->type == SOCKET_UDP) hints->ai_socktype = SOCK_DGRAM; - else hints->ai_socktype = SOCK_STREAM; - - - // Set passive unless UDP client - if (!(config->type == SOCKET_UDP) || config->server) hints->ai_flags = AI_PASSIVE; -} - -//---------------------------------------------------------------------------------- -// Module implementation -//---------------------------------------------------------------------------------- - -// Initialise the network (requires for windows platforms only) -bool InitNetworkDevice(void) -{ -#if defined(_WIN32) - WORD wVersionRequested; - WSADATA wsaData; - - wVersionRequested = MAKEWORD(2, 2); - int err = WSAStartup(wVersionRequested, &wsaData); - - if (err != 0) - { - TRACELOG(LOG_WARNING, "WinSock failed to initialise."); - return false; - } - else TRACELOG(LOG_INFO, "WinSock initialised."); - - if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) - { - TRACELOG(LOG_WARNING, "WinSock failed to initialise."); - WSACleanup(); - return false; - } - - return true; -#else - return true; -#endif -} - -// Cleanup, and close the network -void CloseNetworkDevice(void) -{ -#if defined(_WIN32) - WSACleanup(); -#endif -} - -// Protocol-independent name resolution from an address to an ANSI host name -// and from a port number to the ANSI service name. -// -// The flags parameter can be used to customize processing of the getnameinfo function -// -// The following flags are available: -// -// NAME_INFO_DEFAULT 0x00 // No flags set -// NAME_INFO_NOFQDN 0x01 // Only return nodename portion for local hosts -// NAME_INFO_NUMERICHOST 0x02 // Return numeric form of the host's address -// NAME_INFO_NAMEREQD 0x04 // Error if the host's name not in DNS -// NAME_INFO_NUMERICSERV 0x08 // Return numeric form of the service (port #) -// NAME_INFO_DGRAM 0x10 // Service is a datagram service -void ResolveIP(const char *ip, const char *port, int flags, char *host, char *serv) -{ - // Variables - int status; // Status value to return (0) is success - struct addrinfo hints; // Address flags (IPV4, IPV6, UDP?) - struct addrinfo *res; // A pointer to the resulting address list - - // Set the hints - memset(&hints, 0, sizeof hints); - hints.ai_family = AF_UNSPEC; // Either IPv4 or IPv6 (AF_INET, AF_INET6) - hints.ai_protocol = 0; // Automatically select correct protocol (IPPROTO_TCP), (IPPROTO_UDP) - - // Populate address information - status = getaddrinfo(ip, // e.g. "www.example.com" or IP - port, // e.g. "http" or port number - &hints, // e.g. SOCK_STREAM/SOCK_DGRAM - &res // The struct to populate - ); - - // Did we succeed? - if (status != 0) TRACELOG(LOG_WARNING, "Failed to get resolve host %s:%s: %s", ip, port, gai_strerror(errno)); - else TRACELOG(LOG_DEBUG, "Resolving... %s::%s", ip, port); - - // Attempt to resolve network byte order ip to hostname - switch (res->ai_family) - { - case AF_INET: - { - status = getnameinfo(&*((struct sockaddr *)res->ai_addr), - sizeof(*((struct sockaddr_in *)res->ai_addr)), - host, NI_MAXHOST, serv, NI_MAXSERV, flags); - } break; - case AF_INET6: - { - /* - status = getnameinfo(&*((struct sockaddr_in6 *)res->ai_addr), // TODO. - sizeof(*((struct sockaddr_in6 *)res->ai_addr)), - host, NI_MAXHOST, serv, NI_MAXSERV, flags); - */ - } break; - default: break; - } - - if (status != 0) TRACELOG(LOG_WARNING, "Failed to resolve ip %s: %s", ip, SocketGetLastErrorString()); - else TRACELOG(LOG_DEBUG, "Successfully resolved %s::%s to %s", ip, port, host); - - // Free the pointer to the data returned by addrinfo - freeaddrinfo(res); -} - -// Protocol-independent translation from an ANSI host name to an address -// -// e.g. -// const char* address = "127.0.0.1" (local address) -// const char* port = "80" -// -// Parameters: -// const char* address - A pointer to a NULL-terminated ANSI string that contains a host (node) name or a numeric host address string. -// const char* service - A pointer to a NULL-terminated ANSI string that contains either a service name or port number represented as a string. -// -// Returns: -// The total amount of addresses found, -1 on error -// -int ResolveHost(const char *address, const char *service, int addressType, int flags, AddressInformation *outAddr) -{ - // Variables - int status; // Status value to return (0) is success - struct addrinfo hints; // Address flags (IPV4, IPV6, UDP?) - struct addrinfo *res; // will point to the results - struct addrinfo *iterator; - assert(((address != NULL || address != 0) || (service != NULL || service != 0))); - assert(((addressType == AF_INET) || (addressType == AF_INET6) || (addressType == AF_UNSPEC))); - - // Set the hints - memset(&hints, 0, sizeof hints); - hints.ai_family = addressType; // Either IPv4 or IPv6 (ADDRESS_TYPE_IPV4, ADDRESS_TYPE_IPV6) - hints.ai_protocol = 0; // Automatically select correct protocol (IPPROTO_TCP), (IPPROTO_UDP) - hints.ai_flags = flags; - assert((hints.ai_addrlen == 0) || (hints.ai_addrlen == 0)); - assert((hints.ai_canonname == 0) || (hints.ai_canonname == 0)); - assert((hints.ai_addr == 0) || (hints.ai_addr == 0)); - assert((hints.ai_next == 0) || (hints.ai_next == 0)); - - // When the address is NULL, populate the IP for me - if (address == NULL) - { - if ((hints.ai_flags & AI_PASSIVE) == 0) hints.ai_flags |= AI_PASSIVE; - } - - TRACELOG(LOG_INFO, "Resolving host..."); - - // Populate address information - status = getaddrinfo(address, // e.g. "www.example.com" or IP - service, // e.g. "http" or port number - &hints, // e.g. SOCK_STREAM/SOCK_DGRAM - &res // The struct to populate - ); - - // Did we succeed? - if (status != 0) - { - int error = SocketGetLastError(); - SocketSetLastError(0); - TRACELOG(LOG_WARNING, "Failed to get resolve host: %s", SocketErrorCodeToString(error)); - return -1; - } - else TRACELOG(LOG_INFO, "Successfully resolved host %s:%s", address, service); - - // Calculate the size of the address information list - int size = 0; - for (iterator = res; iterator != NULL; iterator = iterator->ai_next) size++; - - // Validate the size is > 0, otherwise return - if (size <= 0) - { - TRACELOG(LOG_WARNING, "Error, no addresses found."); - return -1; - } - - // If not address list was allocated, allocate it dynamically with the known address size - if (outAddr == NULL) outAddr = (AddressInformation *)RNET_MALLOC(size * sizeof(AddressInformation)); - - // Dynamically allocate an array of address information structs - if (outAddr != NULL) - { - int i; - for (i = 0; i < size; ++i) - { - outAddr[i] = LoadAddress(); - if (outAddr[i] == NULL) - { - break; - } - } - - outAddr[i] = NULL; - if (i != size) outAddr = NULL; - } - else - { - TRACELOG(LOG_WARNING, "Error, failed to dynamically allocate memory for the address list"); - return -1; - } - - // Copy all the address information from res into outAddrList - int i = 0; - for (iterator = res; iterator != NULL; iterator = iterator->ai_next) - { - if (i < size) - { - outAddr[i]->addr.ai_flags = iterator->ai_flags; - outAddr[i]->addr.ai_family = iterator->ai_family; - outAddr[i]->addr.ai_socktype = iterator->ai_socktype; - outAddr[i]->addr.ai_protocol = iterator->ai_protocol; - outAddr[i]->addr.ai_addrlen = iterator->ai_addrlen; - *outAddr[i]->addr.ai_addr = *iterator->ai_addr; -#if NET_DEBUG_ENABLED - TRACELOG(LOG_DEBUG, "GetAddressInformation"); - TRACELOG(LOG_DEBUG, "\tFlags: 0x%x", iterator->ai_flags); - //PrintSocket(outAddr[i]->addr.ai_addr, outAddr[i]->addr.ai_family, outAddr[i]->addr.ai_socktype, outAddr[i]->addr.ai_protocol); - TRACELOG(LOG_DEBUG, "Length of this sockaddr: %d", outAddr[i]->addr.ai_addrlen); - TRACELOG(LOG_DEBUG, "Canonical name: %s", iterator->ai_canonname); -#endif - i++; - } - } - - // Free the pointer to the data returned by addrinfo - freeaddrinfo(res); - - // Return the total count of addresses found - return size; -} - -// This here is the bread and butter of the socket API, This function will -// attempt to open a socket, bind and listen to it based on the config passed in -// -// SocketConfig* config - Configuration for which socket to open -// SocketResult* result - The results of this function (if any, including errors) -// -// e.g. -// SocketConfig server_config = { SocketConfig client_config = { -// .host = "127.0.0.1", .host = "127.0.0.1", -// .port = 8080, .port = 8080, -// .server = true, }; -// .nonblocking = true, -// }; -// SocketResult server_res; SocketResult client_res; -bool SocketCreate(SocketConfig *config, SocketResult *result) -{ - // Socket creation result - bool success = true; - - // Make sure we've not received a null config or result pointer - if (config == NULL || result == NULL) return (success = false); - - // Set the defaults based on the config - if (!SocketSetDefaults(config)) - { - TRACELOG(LOG_WARNING, "Configuration Error."); - success = false; - } - else - { - // Create the socket - if (CreateSocket(config, result)) - { - if (config->nonblocking) SocketSetNonBlocking(result->socket); - else SocketSetBlocking(result->socket); - } - else success = false; - } - - return success; -} - -// Bind a socket to a local address -// Note: The bind function is required on an unconnected socket before subsequent calls to the listen function. -bool SocketBind(SocketConfig *config, SocketResult *result) -{ - bool success = false; - result->status = RESULT_FAILURE; - struct sockaddr_storage *sock_addr = NULL; - - // Don't bind to a socket that isn't configured as a server - if (!IsSocketValid(result->socket) || !config->server) - { - TRACELOG(LOG_WARNING, Cannot bind to socket marked as \"Client\" in SocketConfig."); - success = false; - } - else - { - if (result->socket->isIPv6) sock_addr = (struct sockaddr_storage *)&result->socket->addripv6->address; - else sock_addr = (struct sockaddr_storage *)&result->socket->addripv4->address; - - if (sock_addr != NULL) - { - if (bind(result->socket->channel, (struct sockaddr *)sock_addr, sizeof(*sock_addr)) != SOCKET_ERROR) - { - TRACELOG(LOG_INFO, "Successfully bound socket."); - success = true; - } - else - { - result->socket->status = SocketGetLastError(); - TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(result->socket->status)); - SocketSetLastError(0); - success = false; - } - } - } - - // Was the bind a success? - if (success) - { - result->status = RESULT_SUCCESS; - result->socket->ready = 0; - result->socket->status = 0; - socklen_t sock_len = sizeof(*sock_addr); - - if (getsockname(result->socket->channel, (struct sockaddr *)sock_addr, &sock_len) < 0) - { - TRACELOG(LOG_WARNING, "Couldn't get socket address"); - } - else - { - struct sockaddr_in *s = (struct sockaddr_in *)sock_addr; - // result->socket->address.host = s->sin_addr.s_addr; - // result->socket->address.port = s->sin_port; - - result->socket->addripv4 = (struct _SocketAddressIPv4 *)RNET_MALLOC(sizeof(*result->socket->addripv4)); - - if (result->socket->addripv4 != NULL) memset(result->socket->addripv4, 0, sizeof(*result->socket->addripv4)); - - memcpy(&result->socket->addripv4->address, (struct sockaddr_in *)&s->sin_addr, sizeof(struct sockaddr_in)); - } - } - return success; -} - -// Listens (and queues) incoming connections requests for a bound port. -bool SocketListen(SocketConfig *config, SocketResult *result) -{ - bool success = false; - result->status = RESULT_FAILURE; - - // Don't bind to a socket that isn't configured as a server - if (!IsSocketValid(result->socket) || !config->server) - { - TRACELOG(LOG_WARNING, "Cannot listen on socket marked as \"Client\" in SocketConfig."); - success = false; - } - else - { - // Don't listen on UDP sockets - if (!(config->type == SOCKET_UDP)) - { - if (listen(result->socket->channel, config->backlog_size) != SOCKET_ERROR) - { - TRACELOG(LOG_INFO, "Started listening on socket..."); - success = true; - } - else - { - success = false; - result->socket->status = SocketGetLastError(); - TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(result->socket->status)); - SocketSetLastError(0); - } - } - else - { - TRACELOG(LOG_WARNING, "Cannot listen on socket marked as \"UDP\" (datagram) in SocketConfig."); - success = false; - } - } - - // Was the listen a success? - if (success) - { - result->status = RESULT_SUCCESS; - result->socket->ready = 0; - result->socket->status = 0; - } - - return success; -} - -// Connect the socket to the destination specified by "host" and "port" in SocketConfig -bool SocketConnect(SocketConfig *config, SocketResult *result) -{ - bool success = true; - result->status = RESULT_FAILURE; - - // Only bind to sockets marked as server - if (config->server) - { - TRACELOG(LOG_WARNING, "Cannot connect to socket marked as \"Server\" in SocketConfig."); - success = false; - } - else - { - if (IsIPv4Address(config->host)) - { - struct sockaddr_in ip4addr; - ip4addr.sin_family = AF_INET; - unsigned long hport; - hport = strtoul(config->port, NULL, 0); - ip4addr.sin_port = (unsigned short)(hport); - - // TODO: Changed the code to avoid the usage of inet_pton and inet_ntop replacing them with getnameinfo (that should have a better support on windows). - - //inet_pton(AF_INET, config->host, &ip4addr.sin_addr); - int connect_result = connect(result->socket->channel, (struct sockaddr *)&ip4addr, sizeof(ip4addr)); - - if (connect_result == SOCKET_ERROR) - { - result->socket->status = SocketGetLastError(); - SocketSetLastError(0); - - switch (result->socket->status) - { - case WSAEWOULDBLOCK: success = true; break; - default: - { - TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(result->socket->status)); - success = false; - } break; - } - } - else - { - TRACELOG(LOG_INFO, "Successfully connected to socket."); - success = true; - } - } - else - { - if (IsIPv6Address(config->host)) - { - struct sockaddr_in6 ip6addr; - ip6addr.sin6_family = AF_INET6; - unsigned long hport; - hport = strtoul(config->port, NULL, 0); - ip6addr.sin6_port = htons((unsigned short)hport); - //inet_pton(AF_INET6, config->host, &ip6addr.sin6_addr); // TODO. - int connect_result = connect(result->socket->channel, (struct sockaddr *)&ip6addr, sizeof(ip6addr)); - - if (connect_result == SOCKET_ERROR) - { - result->socket->status = SocketGetLastError(); - SocketSetLastError(0); - - switch (result->socket->status) - { - case WSAEWOULDBLOCK: success = true; break; - default: - { - TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(result->socket->status)); - success = false; - } break; - } - } - else - { - TRACELOG(LOG_INFO, "Successfully connected to socket."); - success = true; - } - } - } - } - - if (success) - { - result->status = RESULT_SUCCESS; - result->socket->ready = 0; - result->socket->status = 0; - } - - return success; -} - -// Closes an existing socket -// -// SocketChannel socket - The id of the socket to close -void SocketClose(Socket *sock) -{ - if (sock != NULL) - { - if (sock->channel != INVALID_SOCKET) closesocket(sock->channel); - } -} - -// Returns the sockaddress for a specific socket in a generic storage struct -SocketAddressStorage SocketGetPeerAddress(Socket *sock) -{ - // TODO. - /* - if (sock->isServer) return NULL; - if (sock->isIPv6) return sock->addripv6; - else return sock->addripv4; - */ - - return NULL; -} - -// Return the address-type appropriate host portion of a socket address -const char *GetSocketAddressHost(SocketAddressStorage storage) -{ - assert(storage->address.ss_family == AF_INET || storage->address.ss_family == AF_INET6); - return SocketAddressToString((struct sockaddr_storage *)storage); -} - -// Return the address-type appropriate port(service) portion of a socket address -short GetSocketAddressPort(SocketAddressStorage storage) -{ - //return ntohs(GetSocketPortPtr(storage)); // TODO. - - return 0; -} - -// The accept function permits an incoming connection attempt on a socket. -// -// SocketChannel listener - The socket to listen for incoming connections on (i.e. server) -// SocketResult* out - The result of this function (if any, including errors) -// -// e.g. -// -// SocketResult connection; -// bool connected = false; -// if (!connected) -// { -// if (SocketAccept(server_res.socket.channel, &connection)) -// { -// connected = true; -// } -// } -Socket *SocketAccept(Socket *server, SocketConfig *config) -{ - if (!server->isServer || server->type == SOCKET_UDP) return NULL; - - struct sockaddr_storage sock_addr; - socklen_t sock_alen; - Socket *sock = LoadSocket(); - server->ready = 0; - sock_alen = sizeof(sock_addr); - sock->channel = accept(server->channel, (struct sockaddr *)&sock_addr, &sock_alen); - - if (sock->channel == INVALID_SOCKET) - { - sock->status = SocketGetLastError(); - TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(sock->status)); - SocketSetLastError(0); - SocketClose(sock); - - return NULL; - } - - (config->nonblocking) ? SocketSetNonBlocking(sock) : SocketSetBlocking(sock); - sock->isServer = false; - sock->ready = 0; - sock->type = server->type; - - switch (sock_addr.ss_family) - { - case AF_INET: - { - struct sockaddr_in *s = ((struct sockaddr_in *)&sock_addr); - sock->addripv4 = (struct _SocketAddressIPv4 *)RNET_MALLOC(sizeof(*sock->addripv4)); - - if (sock->addripv4 != NULL) - { - memset(sock->addripv4, 0, sizeof(*sock->addripv4)); - memcpy(&sock->addripv4->address, (struct sockaddr_in *)&s->sin_addr, sizeof(struct sockaddr_in)); - TRACELOG(LOG_INFO, "Server: Got connection from %s::%hu", SocketAddressToString((struct sockaddr_storage *)s), ntohs(sock->addripv4->address.sin_port)); - } - } break; - case AF_INET6: - { - struct sockaddr_in6 *s = ((struct sockaddr_in6 *)&sock_addr); - sock->addripv6 = (struct _SocketAddressIPv6 *)RNET_MALLOC(sizeof(*sock->addripv6)); - - if (sock->addripv6 != NULL) - { - memset(sock->addripv6, 0, sizeof(*sock->addripv6)); - memcpy(&sock->addripv6->address, (struct sockaddr_in6 *)&s->sin6_addr, sizeof(struct sockaddr_in6)); - TRACELOG(LOG_INFO, "Server: Got connection from %s::%hu", SocketAddressToString((struct sockaddr_storage *)s), ntohs(sock->addripv6->address.sin6_port)); - } - } break; - } - - return sock; -} - -// Verify that the channel is in the valid range -static int ValidChannel(int channel) -{ - if ((channel < 0) || (channel >= SOCKET_MAX_UDPCHANNELS)) - { - TRACELOG(LOG_WARNING, "Invalid channel"); - return 0; - } - - return 1; -} - -// Set the socket channel -int SocketSetChannel(Socket *socket, int channel, const IPAddress *address) -{ - struct UDPChannel *binding; - - if (socket == NULL) - { - TRACELOG(LOG_WARNING, "Passed a NULL socket"); - return (-1); - } - - if (channel == -1) - { - for (channel = 0; channel < SOCKET_MAX_UDPCHANNELS; ++channel) - { - binding = &socket->binding[channel]; - if (binding->numbound < SOCKET_MAX_UDPADDRESSES) break; - } - } - else - { - if (!ValidChannel(channel)) return (-1); - - binding = &socket->binding[channel]; - } - - if (binding->numbound == SOCKET_MAX_UDPADDRESSES) - { - TRACELOG(LOG_WARNING, "No room for new addresses"); - return (-1); - } - - binding->address[binding->numbound++] = *address; - - return (channel); -} - -// Remove the socket channel -void SocketUnsetChannel(Socket *socket, int channel) -{ - if ((channel >= 0) && (channel < SOCKET_MAX_UDPCHANNELS)) socket->binding[channel].numbound = 0; -} - -/* Allocate/free a single UDP packet 'size' bytes long. - The new packet is returned, or NULL if the function ran out of memory. - */ -SocketDataPacket *AllocPacket(int size) -{ - SocketDataPacket *packet = (SocketDataPacket *)RNET_MALLOC(sizeof(*packet)); - int error = 1; - - if (packet != NULL) - { - packet->maxlen = size; - packet->data = (uint8_t *)RNET_MALLOC(size); - if (packet->data != NULL) - { - error = 0; - } - } - - if (error) - { - FreePacket(packet); - packet = NULL; - } - - return (packet); -} - -int ResizePacket(SocketDataPacket *packet, int newsize) -{ - uint8_t *newdata = (uint8_t *)RNET_MALLOC(newsize); - - if (newdata != NULL) - { - RNET_FREE(packet->data); - packet->data = newdata; - packet->maxlen = newsize; - } - - return (packet->maxlen); -} - -void FreePacket(SocketDataPacket *packet) -{ - if (packet) - { - RNET_FREE(packet->data); - RNET_FREE(packet); - } -} - -// Allocate/Free a UDP packet vector (array of packets) of 'howmany' packets, each 'size' bytes long. -// A pointer to the packet array is returned, or NULL if the function ran out of memory. -SocketDataPacket **AllocPacketList(int howmany, int size) -{ - SocketDataPacket **packetV = (SocketDataPacket **)RNET_MALLOC((howmany + 1) * sizeof(*packetV)); - - if (packetV != NULL) - { - int i; - for (i = 0; i < howmany; ++i) - { - packetV[i] = AllocPacket(size); - if (packetV[i] == NULL) - { - break; - } - } - packetV[i] = NULL; - - if (i != howmany) - { - FreePacketList(packetV); - packetV = NULL; - } - } - - return (packetV); -} - -void FreePacketList(SocketDataPacket **packetV) -{ - if (packetV) - { - for (int i = 0; packetV[i]; ++i) FreePacket(packetV[i]); - RNET_FREE(packetV); - } -} - -// Send 'len' bytes of 'data' over the non-server socket 'sock' -int SocketSend(Socket *sock, const void *datap, int length) -{ - int sent = 0; - int left = length; - int status = -1; - int numsent = 0; - const unsigned char *data = (const unsigned char *)datap; - - // Server sockets are for accepting connections only - if (sock->isServer) - { - TRACELOG(LOG_WARNING, "Cannot send information on a server socket"); - return -1; - } - - // Which socket are we trying to send data on - switch (sock->type) - { - case SOCKET_TCP: - { - SocketSetLastError(0); - do - { - length = send(sock->channel, (const char *)data, left, 0); - if (length > 0) - { - sent += length; - left -= length; - data += length; - } - } while ((left > 0) && // While we still have bytes left to send - ((length > 0) || // The amount of bytes we actually sent is > 0 - (SocketGetLastError() == WSAEINTR)) // The socket was interupted - ); - - if (length == SOCKET_ERROR) - { - sock->status = SocketGetLastError(); - TRACELOG(LOG_DEBUG, "Socket Error: %s", SocketErrorCodeToString(sock->status)); - SocketSetLastError(0); - } - else TRACELOG(LOG_DEBUG, "Successfully sent \"%s\" (%d bytes)", datap, sent); - - return sent; - } break; - case SOCKET_UDP: - { - SocketSetLastError(0); - - if (sock->isIPv6) status = sendto(sock->channel, (const char *)data, left, 0, (struct sockaddr *)&sock->addripv6->address, sizeof(sock->addripv6->address)); - else status = sendto(sock->channel, (const char *)data, left, 0, (struct sockaddr *)&sock->addripv4->address, sizeof(sock->addripv4->address)); - - if (sent >= 0) - { - sock->status = 0; - ++numsent; - TRACELOG(LOG_DEBUG, "Successfully sent \"%s\" (%d bytes)", datap, status); - } - else - { - sock->status = SocketGetLastError(); - TRACELOG(LOG_DEBUG, "Socket Error: %s", SocketGetLastErrorString(sock->status)); - SocketSetLastError(0); - return 0; - } - - return numsent; - } break; - default: break; - } - - return -1; -} - -// Receive up to 'maxlen' bytes of data over the non-server socket 'sock', -// and store them in the buffer pointed to by 'data'. -// This function returns the actual amount of data received. If the return -// value is less than or equal to zero, then either the remote connection was -// closed, or an unknown socket error occurred. -int SocketReceive(Socket *sock, void *data, int maxlen) -{ - int len = 0; - int numrecv = 0; - int status = 0; - socklen_t sock_len; - struct sockaddr_storage sock_addr; - //char ip[INET6_ADDRSTRLEN]; - - // Server sockets are for accepting connections only - if (sock->isServer && (sock->type == SOCKET_TCP)) - { - sock->status = SocketGetLastError(); - TRACELOG(LOG_DEBUG, "Socket Error: %s", "Server sockets cannot be used to receive data"); - SocketSetLastError(0); - return 0; - } - - // Which socket are we trying to send data on - switch (sock->type) - { - case SOCKET_TCP: - { - SocketSetLastError(0); - do - { - len = recv(sock->channel, (char *)data, maxlen, 0); - } while (SocketGetLastError() == WSAEINTR); - - if (len > 0) - { - // Who sent the packet? - if (sock->type == SOCKET_UDP) - { - //TRACELOG(LOG_DEBUG, "Received data from: %s", inet_ntop(sock_addr.ss_family, GetSocketAddressPtr((struct sockaddr *)&sock_addr), ip, sizeof(ip))); - } - - ((unsigned char *)data)[len] = '\0'; // Add null terminating character to the end of the stream - TRACELOG(LOG_DEBUG, "Received \"%s\" (%d bytes)", data, len); - } - - sock->ready = 0; - return len; - } break; - case SOCKET_UDP: - { - SocketSetLastError(0); - sock_len = sizeof(sock_addr); - status = recvfrom(sock->channel, // The receving channel - data, // A pointer to the data buffer to fill - maxlen, // The max length of the data to fill - 0, // Flags - (struct sockaddr *)&sock_addr, // The address of the recevied data - &sock_len // The length of the received data address - ); - - if (status >= 0) ++numrecv; - else - { - sock->status = SocketGetLastError(); - - switch (sock->status) - { - case WSAEWOULDBLOCK: break; - default: TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(sock->status)); break; - } - - SocketSetLastError(0); - return 0; - } - - sock->ready = 0; - return numrecv; - } break; - default: break; - } - - return -1; -} - -// Does the socket have it's 'ready' flag set? -bool IsSocketReady(Socket *sock) -{ - return (sock != NULL) && (sock->ready); -} - -// Check if the socket is considered connected -bool IsSocketConnected(Socket *sock) -{ -#if defined(_WIN32) - FD_SET writefds; - FD_ZERO(&writefds); - FD_SET(sock->channel, &writefds); - struct timeval timeout; - timeout.tv_sec = 1; - timeout.tv_usec = 1000000000UL; - int total = select(0, NULL, &writefds, NULL, &timeout); - - if (total == -1) - { // Error - sock->status = SocketGetLastError(); - TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(sock->status)); - SocketSetLastError(0); - } - else if (total == 0) return false; // Timeout - else if (FD_ISSET(sock->channel, &writefds)) return true; - - return false; -#else - return true; -#endif -} - -// Allocate and return a SocketResult struct -SocketResult *LoadSocketResult(void) -{ - struct SocketResult *res = (struct SocketResult *)RNET_MALLOC(sizeof(*res)); - - if (res != NULL) - { - memset(res, 0, sizeof(*res)); - if ((res->socket = LoadSocket()) == NULL) - { - RNET_FREE(res); - res = NULL; - } - } - - return res; -} - -// Free an allocated SocketResult -void UnloadSocketResult(SocketResult **result) -{ - if (*result != NULL) - { - if ((*result)->socket != NULL) UnloadSocket(&((*result)->socket)); - - RNET_FREE(*result); - *result = NULL; - } -} - -// Allocate a Socket -Socket *LoadSocket(void) -{ - struct Socket *sock; - sock = (Socket *)RNET_MALLOC(sizeof(*sock)); - - if (sock != NULL) memset(sock, 0, sizeof(*sock)); - else - { - TRACELOG(LOG_WARNING, "Ran out of memory attempting to allocate a socket"); - SocketClose(sock); - RNET_FREE(sock); - sock = NULL; - } - - return sock; -} - -// Free an allocated Socket -void UnloadSocket(Socket **sock) -{ - if (*sock != NULL) - { - RNET_FREE(*sock); - *sock = NULL; - } -} - -// Allocate a SocketSet -SocketSet *LoadSocketSet(int max) -{ - struct SocketSet *set = (struct SocketSet *)RNET_MALLOC(sizeof(*set)); - - if (set != NULL) - { - set->numsockets = 0; - set->maxsockets = max; - set->sockets = (struct Socket **)RNET_MALLOC(max * sizeof(*set->sockets)); - if (set->sockets != NULL) - { - for (int i = 0; i < max; ++i) set->sockets[i] = NULL; - } - else - { - RNET_FREE(set); - set = NULL; - } - } - - return (set); -} - -// Free an allocated SocketSet -void UnloadSocketSet(SocketSet *set) -{ - if (set) - { - RNET_FREE(set->sockets); - RNET_FREE(set); - } -} - -// Add a Socket "sock" to the SocketSet "set" -int AddSocket(SocketSet *set, Socket *sock) -{ - if (sock != NULL) - { - if (set->numsockets == set->maxsockets) - { - TRACELOG(LOG_DEBUG, "Socket Error: %s", "SocketSet is full"); - SocketSetLastError(0); - return (-1); - } - set->sockets[set->numsockets++] = (struct Socket *)sock; - } - else - { - TRACELOG(LOG_DEBUG, "Socket Error: %s", "Socket was null"); - SocketSetLastError(0); - return (-1); - } - - return (set->numsockets); -} - -// Remove a Socket "sock" to the SocketSet "set" -int RemoveSocket(SocketSet *set, Socket *sock) -{ - if (sock != NULL) - { - int i = 0; - for (i = 0; i < set->numsockets; ++i) - { - if (set->sockets[i] == (struct Socket *)sock) break; - } - - if (i == set->numsockets) - { - TRACELOG(LOG_DEBUG, "Socket Error: %s", "Socket not found"); - SocketSetLastError(0); - return (-1); - } - - --set->numsockets; - for (; i < set->numsockets; ++i) set->sockets[i] = set->sockets[i + 1]; - } - - return (set->numsockets); -} - -// Check the sockets in the socket set for pending information -int CheckSockets(SocketSet *set, unsigned int timeout) -{ - int i; - SOCKET maxfd; - int retval; - struct timeval tv; - fd_set mask; - - /* Find the largest file descriptor */ - maxfd = 0; - for (i = set->numsockets - 1; i >= 0; --i) - { - if (set->sockets[i]->channel > maxfd) - { - maxfd = set->sockets[i]->channel; - } - } - - // Check the file descriptors for available data - do - { - SocketSetLastError(0); - - // Set up the mask of file descriptors - FD_ZERO(&mask); - for (i = set->numsockets - 1; i >= 0; --i) - { - FD_SET(set->sockets[i]->channel, &mask); - } // Set up the timeout - - tv.tv_sec = timeout / 1000; - tv.tv_usec = (timeout % 1000) * 1000; - - /* Look! */ - retval = select(maxfd + 1, &mask, NULL, NULL, &tv); - } while (SocketGetLastError() == WSAEINTR); - - // Mark all file descriptors ready that have data available - if (retval > 0) - { - for (i = set->numsockets - 1; i >= 0; --i) - { - if (FD_ISSET(set->sockets[i]->channel, &mask)) set->sockets[i]->ready = 1; - } - } - - return retval; -} - -// Allocate an AddressInformation -AddressInformation LoadAddress(void) -{ - AddressInformation addressInfo = NULL; - addressInfo = (AddressInformation)RNET_CALLOC(1, sizeof(*addressInfo)); - - if (addressInfo != NULL) - { - addressInfo->addr.ai_addr = (struct sockaddr *)RNET_CALLOC(1, sizeof(struct sockaddr)); - if (addressInfo->addr.ai_addr == NULL) TRACELOG(LOG_WARNING, "Failed to allocate memory for \"struct sockaddr\""); - } - else TRACELOG(LOG_WARNING, "Failed to allocate memory for \"struct AddressInformation\""); - - return addressInfo; -} - -// Free an AddressInformation struct -void UnloadAddress(AddressInformation *addressInfo) -{ - if (*addressInfo != NULL) - { - if ((*addressInfo)->addr.ai_addr != NULL) - { - RNET_FREE((*addressInfo)->addr.ai_addr); - (*addressInfo)->addr.ai_addr = NULL; - } - - RNET_FREE(*addressInfo); - *addressInfo = NULL; - } -} - -// Allocate a list of AddressInformation -AddressInformation *LoadAddressList(int size) -{ - AddressInformation *addr; - addr = (AddressInformation *)RNET_MALLOC(size * sizeof(AddressInformation)); - return addr; -} - -// Opaque datatype accessor addrinfo->ai_family -int GetAddressFamily(AddressInformation address) -{ - return address->addr.ai_family; -} - -// Opaque datatype accessor addrinfo->ai_socktype -int GetAddressSocketType(AddressInformation address) -{ - return address->addr.ai_socktype; -} - -// Opaque datatype accessor addrinfo->ai_protocol -int GetAddressProtocol(AddressInformation address) -{ - return address->addr.ai_protocol; -} - -// Opaque datatype accessor addrinfo->ai_canonname -char *GetAddressCanonName(AddressInformation address) -{ - return address->addr.ai_canonname; -} - -// Opaque datatype accessor addrinfo->ai_addr -char *GetAddressHostAndPort(AddressInformation address, char *outhost, unsigned short *outport) -{ - //char *ip[INET6_ADDRSTRLEN]; - char *result = NULL; - struct sockaddr_storage *storage = (struct sockaddr_storage *)address->addr.ai_addr; - - switch (storage->ss_family) - { - case AF_INET: - { - struct sockaddr_in *s = ((struct sockaddr_in *)address->addr.ai_addr); - //result = inet_ntop(AF_INET, &s->sin_addr, ip, INET_ADDRSTRLEN); // TODO. - *outport = ntohs(s->sin_port); - } break; - case AF_INET6: - { - struct sockaddr_in6 *s = ((struct sockaddr_in6 *)address->addr.ai_addr); - //result = inet_ntop(AF_INET6, &s->sin6_addr, ip, INET6_ADDRSTRLEN); // TODO. - *outport = ntohs(s->sin6_port); - } break; - default: break; - } - - if (result == NULL) - { - TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(SocketGetLastError())); - SocketSetLastError(0); - } - else - { - strcpy(outhost, result); - } - return result; -} - -// -void PacketSend(Packet *packet) -{ - TRACELOG(LOG_INFO, "Sending packet (%s) with size %d\n", packet->data, packet->size); -} - -// -void PacketReceive(Packet *packet) -{ - TRACELOG(LOG_INFO, "Receiving packet (%s) with size %d\n", packet->data, packet->size); -} - -// -void PacketWrite16(Packet *packet, uint16_t value) -{ - TRACELOG(LOG_INFO, "Original: 0x%04" PRIX16 " - %" PRIu16 "\n", value, value); - uint8_t *data = packet->data + packet->offs; - *data++ = (uint8_t)(value >> 8); - *data++ = (uint8_t)(value); - packet->size += sizeof(uint16_t); - packet->offs += sizeof(uint16_t); - TRACELOG(LOG_INFO, "Network: 0x%04" PRIX16 " - %" PRIu16 "\n", (uint16_t) *data, (uint16_t) *data); -} - -// -void PacketWrite32(Packet *packet, uint32_t value) -{ - TRACELOG(LOG_INFO, "Original: 0x%08" PRIX32 " - %" PRIu32 "\n", value, value); - uint8_t *data = packet->data + packet->offs; - *data++ = (uint8_t)(value >> 24); - *data++ = (uint8_t)(value >> 16); - *data++ = (uint8_t)(value >> 8); - *data++ = (uint8_t)(value); - packet->size += sizeof(uint32_t); - packet->offs += sizeof(uint32_t); - - TRACELOG(LOG_INFO, "Network: 0x%08" PRIX32 " - %" PRIu32 "\n", - (uint32_t)(((intptr_t) packet->data) - packet->offs), - (uint32_t)(((intptr_t) packet->data) - packet->offs)); -} - -// -void PacketWrite64(Packet *packet, uint64_t value) -{ - TRACELOG(LOG_INFO, "Original: 0x%016" PRIX64 " - %" PRIu64 "\n", value, value); - - uint8_t *data = packet->data + packet->offs; - *data++ = (uint8_t)(value >> 56); - *data++ = (uint8_t)(value >> 48); - *data++ = (uint8_t)(value >> 40); - *data++ = (uint8_t)(value >> 32); - *data++ = (uint8_t)(value >> 24); - *data++ = (uint8_t)(value >> 16); - *data++ = (uint8_t)(value >> 8); - *data++ = (uint8_t)(value); - packet->size += sizeof(uint64_t); - packet->offs += sizeof(uint64_t); - - TRACELOG(LOG_INFO, "Network: 0x%016" PRIX64 " - %" PRIu64 "\n", (uint64_t)(packet->data - packet->offs), (uint64_t)(packet->data - packet->offs)); -} - -// -uint16_t PacketRead16(Packet *packet) -{ - uint8_t *data = packet->data + packet->offs; - packet->size += sizeof(uint16_t); - packet->offs += sizeof(uint16_t); - uint16_t value = ((uint16_t) data[0] << 8) | data[1]; - TRACELOG(LOG_INFO, "Original: 0x%04" PRIX16 " - %" PRIu16 "\n", value, value); - - return value; -} - -// -uint32_t PacketRead32(Packet *packet) -{ - uint8_t *data = packet->data + packet->offs; - packet->size += sizeof(uint32_t); - packet->offs += sizeof(uint32_t); - uint32_t value = ((uint32_t) data[0] << 24) | ((uint32_t) data[1] << 16) | ((uint32_t) data[2] << 8) | data[3]; - TRACELOG(LOG_INFO, "Original: 0x%08" PRIX32 " - %" PRIu32 "\n", value, value); - - return value; -} - -// -uint64_t PacketRead64(Packet *packet) -{ - uint8_t *data = packet->data + packet->offs; - packet->size += sizeof(uint64_t); - packet->offs += sizeof(uint64_t); - uint64_t value = ((uint64_t) data[0] << 56) | ((uint64_t) data[1] << 48) | ((uint64_t) data[2] << 40) | ((uint64_t) data[3] << 32) | ((uint64_t) data[4] << 24) | ((uint64_t) data[5] << 16) | ((uint64_t) data[6] << 8) | data[7]; - TRACELOG(LOG_INFO, "Original: 0x%016" PRIX64 " - %" PRIu64 "\n", value, value); - - return value; -} - +/********************************************************************************************** +* +* rnet - A simple and easy-to-use network module for raylib +* +* FEATURES: +* - Provides a simple and (hopefully) easy to use wrapper around the Berkeley socket API +* +* INSPIRED BY: +* SFML Sockets - https://www.sfml-dev.org/documentation/2.5.1/classsf_1_1Socket.php +* SDL_net - https://www.libsdl.org/projects/SDL_net/ +* BSD Sockets - https://www.gnu.org/software/libc/manual/html_node/Sockets.html +* BEEJ - https://beej.us/guide/bgnet/html/single/bgnet.html +* Winsock2 - https://docs.microsoft.com/en-us/windows/desktop/api/winsock2 +* +* CONTRIBUTORS: +* Jak Barnes (github: @syphonx) (Feb. 2019) - Initial version +* +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2019-2020 Jak Barnes (@syphonx) and Ramon Santamaria (@raysan5) +* +* This software is provided "as-is", without any express or implied warranty. In no event +* will the authors be held liable for any damages arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, including commercial +* applications, and to alter it and redistribute it freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not claim that you +* wrote the original software. If you use this software in a product, an acknowledgment +* in the product documentation would be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not be misrepresented +* as being the original software. +* +* 3. This notice may not be removed or altered from any source distribution. +* +**********************************************************************************************/ + +#ifndef RNET_H +#define RNET_H + +#include // Required for limits +#include // Required for platform type sizes + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- + +// Undefine any conflicting windows.h symbols +// If defined, the following flags inhibit definition of the indicated items. +#define NOGDICAPMASKS // CC_*, LC_*, PC_*, CP_*, TC_*, RC_ +#define NOVIRTUALKEYCODES // VK_* +#define NOWINMESSAGES // WM_*, EM_*, LB_*, CB_* +#define NOWINSTYLES // WS_*, CS_*, ES_*, LBS_*, SBS_*, CBS_* +#define NOSYSMETRICS // SM_* +#define NOMENUS // MF_* +#define NOICONS // IDI_* +#define NOKEYSTATES // MK_* +#define NOSYSCOMMANDS // SC_* +#define NORASTEROPS // Binary and Tertiary raster ops +#define NOSHOWWINDOW // SW_* +#define OEMRESOURCE // OEM Resource values +#define NOATOM // Atom Manager routines +#define NOCLIPBOARD // Clipboard routines +#define NOCOLOR // Screen colors +#define NOCTLMGR // Control and Dialog routines +#define NODRAWTEXT // DrawText() and DT_* +#define NOGDI // All GDI defines and routines +#define NOKERNEL // All KERNEL defines and routines +#define NOUSER // All USER defines and routines +#define NONLS // All NLS defines and routines +#define NOMB // MB_* and MessageBox() +#define NOMEMMGR // GMEM_*, LMEM_*, GHND, LHND, associated routines +#define NOMETAFILE // typedef METAFILEPICT +#define NOMINMAX // Macros min(a,b) and max(a,b) +#define NOMSG // typedef MSG and associated routines +#define NOOPENFILE // OpenFile(), OemToAnsi, AnsiToOem, and OF_* +#define NOSCROLL // SB_* and scrolling routines +#define NOSERVICE // All Service Controller routines, SERVICE_ equates, etc. +#define NOSOUND // Sound driver routines +#define NOTEXTMETRIC // typedef TEXTMETRIC and associated routines +#define NOWH // SetWindowsHook and WH_* +#define NOWINOFFSETS // GWL_*, GCL_*, associated routines +#define NOCOMM // COMM driver routines +#define NOKANJI // Kanji support stuff. +#define NOHELP // Help engine interface. +#define NOPROFILER // Profiler interface. +#define NODEFERWINDOWPOS // DeferWindowPos routines +#define NOMCX // Modem Configuration Extensions +#define MMNOSOUND + +// Allow custom memory allocators +#ifndef RNET_MALLOC + #define RNET_MALLOC(sz) malloc(sz) +#endif +#ifndef RNET_CALLOC + #define RNET_CALLOC(n,sz) calloc(n,sz) +#endif +#ifndef RNET_FREE + #define RNET_FREE(p) free(p) +#endif + +//---------------------------------------------------------------------------------- +// Platform type definitions +// From: https://github.com/DFHack/clsocket/blob/master/src/Host.h +//---------------------------------------------------------------------------------- + +#ifdef WIN32 +typedef int socklen_t; +#endif + +#ifndef RESULT_SUCCESS +# define RESULT_SUCCESS 0 +#endif // RESULT_SUCCESS + +#ifndef RESULT_FAILURE +# define RESULT_FAILURE 1 +#endif // RESULT_FAILURE + +#ifndef htonll +# ifdef _BIG_ENDIAN +# define htonll(x) (x) +# define ntohll(x) (x) +# else +# define htonll(x) ((((uint64) htonl(x)) << 32) + htonl(x >> 32)) +# define ntohll(x) ((((uint64) ntohl(x)) << 32) + ntohl(x >> 32)) +# endif // _BIG_ENDIAN +#endif // htonll + +//---------------------------------------------------------------------------------- +// Platform specific network includes +// From: https://github.com/SDL-mirror/SDL_net/blob/master/SDLnetsys.h +//---------------------------------------------------------------------------------- + +// Include system network headers +#if defined(_WIN32) // Windows + #define __USE_W32_SOCKETS + #define WIN32_LEAN_AND_MEAN + #include + #include + #include + #define IPTOS_LOWDELAY 0x10 +#else // Unix + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include +#endif + +#ifndef INVALID_SOCKET + #define INVALID_SOCKET ~(0) +#endif + +#ifndef __USE_W32_SOCKETS + #define closesocket close + #define SOCKET int + #define INVALID_SOCKET -1 + #define SOCKET_ERROR -1 +#endif + +#ifdef __USE_W32_SOCKETS + #ifndef EINTR + #define EINTR WSAEINTR + #endif +#endif + +//---------------------------------------------------------------------------------- +// Module defines +//---------------------------------------------------------------------------------- + +// Network connection related defines +#define SOCKET_MAX_SET_SIZE 32 // Maximum sockets in a set +#define SOCKET_MAX_QUEUE_SIZE 16 // Maximum socket queue size +#define SOCKET_MAX_SOCK_OPTS 4 // Maximum socket options +#define SOCKET_MAX_UDPCHANNELS 32 // Maximum UDP channels +#define SOCKET_MAX_UDPADDRESSES 4 // Maximum bound UDP addresses + +// Network address related defines +#define ADDRESS_IPV4_ADDRSTRLEN 22 // IPv4 string length +#define ADDRESS_IPV6_ADDRSTRLEN 65 // IPv6 string length +#define ADDRESS_TYPE_ANY 0 // AF_UNSPEC +#define ADDRESS_TYPE_IPV4 2 // AF_INET +#define ADDRESS_TYPE_IPV6 23 // AF_INET6 +#define ADDRESS_MAXHOST 1025 // Max size of a fully-qualified domain name +#define ADDRESS_MAXSERV 32 // Max size of a service name + +// Network address related defines +#define ADDRESS_ANY (unsigned long)0x00000000 +#define ADDRESS_LOOPBACK 0x7f000001 +#define ADDRESS_BROADCAST (unsigned long)0xffffffff +#define ADDRESS_NONE 0xffffffff + +// Network resolution related defines +#define NAME_INFO_DEFAULT 0x00 // No flags set +#define NAME_INFO_NOFQDN 0x01 // Only return nodename portion for local hosts +#define NAME_INFO_NUMERICHOST 0x02 // Return numeric form of the host's address +#define NAME_INFO_NAMEREQD 0x04 // Error if the host's name not in DNS +#define NAME_INFO_NUMERICSERV 0x08 // Return numeric form of the service (port #) +#define NAME_INFO_DGRAM 0x10 // Service is a datagram service + +// Address resolution related defines +#if defined(_WIN32) + #define ADDRESS_INFO_PASSIVE (0x00000001) // Socket address will be used in bind() call + #define ADDRESS_INFO_CANONNAME (0x00000002) // Return canonical name in first ai_canonname + #define ADDRESS_INFO_NUMERICHOST (0x00000004) // Nodename must be a numeric address string + #define ADDRESS_INFO_NUMERICSERV (0x00000008) // Servicename must be a numeric port number + #define ADDRESS_INFO_DNS_ONLY (0x00000010) // Restrict queries to unicast DNS only (no LLMNR, netbios, etc.) + #define ADDRESS_INFO_ALL (0x00000100) // Query both IP6 and IP4 with AI_V4MAPPED + #define ADDRESS_INFO_ADDRCONFIG (0x00000400) // Resolution only if global address configured + #define ADDRESS_INFO_V4MAPPED (0x00000800) // On v6 failure, query v4 and convert to V4MAPPED format + #define ADDRESS_INFO_NON_AUTHORITATIVE (0x00004000) // LUP_NON_AUTHORITATIVE + #define ADDRESS_INFO_SECURE (0x00008000) // LUP_SECURE + #define ADDRESS_INFO_RETURN_PREFERRED_NAMES (0x00010000) // LUP_RETURN_PREFERRED_NAMES + #define ADDRESS_INFO_FQDN (0x00020000) // Return the FQDN in ai_canonname + #define ADDRESS_INFO_FILESERVER (0x00040000) // Resolving fileserver name resolution + #define ADDRESS_INFO_DISABLE_IDN_ENCODING (0x00080000) // Disable Internationalized Domain Names handling + #define ADDRESS_INFO_EXTENDED (0x80000000) // Indicates this is extended ADDRINFOEX(2/..) struct + #define ADDRESS_INFO_RESOLUTION_HANDLE (0x40000000) // Request resolution handle +#endif + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- + +// Boolean type +#ifdef _WIN32 + #include +#else +#if defined(__STDC__) && __STDC_VERSION__ >= 199901L + #include +#elif !defined(__cplusplus) && !defined(bool) + typedef enum { false, true } bool; +#endif +#endif + +typedef enum { + SOCKET_TCP = 0, // SOCK_STREAM + SOCKET_UDP = 1 // SOCK_DGRAM +} SocketType; + +// Network typedefs +typedef uint32_t SocketChannel; +typedef struct _AddressInformation *AddressInformation; +typedef struct _SocketAddress *SocketAddress; +typedef struct _SocketAddressIPv4 *SocketAddressIPv4; +typedef struct _SocketAddressIPv6 *SocketAddressIPv6; +typedef struct _SocketAddressStorage *SocketAddressStorage; + +// IPAddress definition (in network byte order) +typedef struct IPAddress { + unsigned long host; // 32-bit IPv4 host address + unsigned short port; // 16-bit protocol port +} IPAddress; + +typedef struct UDPChannel { + int numbound; // The total number of addresses this channel is bound to + IPAddress address[SOCKET_MAX_UDPADDRESSES]; // The list of remote addresses this channel is bound to +} UDPChannel; + +// An option ID, value, sizeof(value) tuple for setsockopt(2). +typedef struct SocketOpt { + int id; // Socked option id + int valueLen; // Socked option value len + void *value; // Socked option value data +} SocketOpt; + +typedef struct Socket { + int ready; // Is the socket ready? i.e. has information + int status; // The last status code to have occured using this socket + bool isServer; // Is this socket a server socket (i.e. TCP/UDP Listen Server) + SocketChannel channel; // The socket handle id + SocketType type; // Is this socket a TCP or UDP socket? + + bool isIPv6; // Is this socket address an ipv6 address? + SocketAddressIPv4 addripv4; // The host/target IPv4 for this socket (in network byte order) + SocketAddressIPv6 addripv6; // The host/target IPv6 for this socket (in network byte order) + + struct UDPChannel binding[SOCKET_MAX_UDPCHANNELS]; // The amount of channels (if UDP) this socket is bound to +} Socket; + +// Configuration for a socket +typedef struct SocketConfig { + SocketType type; // The type of socket, TCP/UDP + char *host; // The host address in xxx.xxx.xxx.xxx form + char *port; // The target port/service in the form "http" or "25565" + bool server; // Listen for incoming clients? + bool nonblocking; // non-blocking operation? + int backlog_size; // set a custom backlog size + SocketOpt sockopts[SOCKET_MAX_SOCK_OPTS]; +} SocketConfig; + +typedef struct SocketDataPacket { + IPAddress address; // The source/dest address of an incoming/outgoing packet + int channel; // The src/dst channel of the packet + int maxlen; // The size of the data buffer + int status; // Packet status after sending + unsigned int len; // The length of the packet data + unsigned char *data; // The packet data +} SocketDataPacket; + +// Result from calling open with a given config +typedef struct SocketResult { + int status; // Socket result state + Socket *socket; // Socket ref +} SocketResult; + +typedef struct SocketSet { + int numsockets; // Socket set count + int maxsockets; // Socket set max + struct Socket **sockets; // Sockets array +} SocketSet; + +// Packet type +typedef struct Packet { + uint32_t size; // The total size of bytes in data + uint32_t offs; // The offset to data access + uint32_t maxs; // The max size of data + uint8_t *data; // Data stored in network byte order +} Packet; + + +#ifdef __cplusplus +extern "C" { // Prevents name mangling of functions +#endif + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +//... + +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- + +// Initialisation and cleanup +bool InitNetworkDevice(void); +void CloseNetworkDevice(void); + +// Address API +void ResolveIP(const char *ip, const char *service, int flags, char *outhost, char *outserv); +int ResolveHost(const char *address, const char *service, int addressType, int flags, AddressInformation *outAddr); +int GetAddressFamily(AddressInformation address); +int GetAddressSocketType(AddressInformation address); +int GetAddressProtocol(AddressInformation address); +char *GetAddressCanonName(AddressInformation address); +char *GetAddressHostAndPort(AddressInformation address, char *outhost, unsigned short *outport); + +// Address Memory API +AddressInformation LoadAddress(void); +void UnloadAddress(AddressInformation *addressInfo); +AddressInformation *LoadAddressList(int size); + +// Socket API +bool SocketCreate(SocketConfig *config, SocketResult *result); +bool SocketBind(SocketConfig *config, SocketResult *result); +bool SocketListen(SocketConfig *config, SocketResult *result); +bool SocketConnect(SocketConfig *config, SocketResult *result); +Socket *SocketAccept(Socket *server, SocketConfig *config); + +// General Socket API +int SocketSend(Socket *sock, const void *datap, int len); +int SocketReceive(Socket *sock, void *data, int maxlen); +SocketAddressStorage SocketGetPeerAddress(Socket *sock); +const char *GetSocketAddressHost(SocketAddressStorage storage); +short GetSocketAddressPort(SocketAddressStorage storage); +void SocketClose(Socket *sock); + +// UDP Socket API +int SocketSetChannel(Socket *socket, int channel, const IPAddress *address); +void SocketUnsetChannel(Socket *socket, int channel); + +// UDP DataPacket API +SocketDataPacket *AllocPacket(int size); +int ResizePacket(SocketDataPacket *packet, int newsize); +void FreePacket(SocketDataPacket *packet); +SocketDataPacket **AllocPacketList(int count, int size); +void FreePacketList(SocketDataPacket **packets); + +// Socket Memory API +Socket *LoadSocket(void); +void UnloadSocket(Socket **sock); +SocketResult *LoadSocketResult(void); +void UnloadSocketResult(SocketResult **result); +SocketSet *LoadSocketSet(int max); +void UnloadSocketSet(SocketSet *sockset); + +// Socket I/O API +bool IsSocketReady(Socket *sock); +bool IsSocketConnected(Socket *sock); +int AddSocket(SocketSet *set, Socket *sock); +int RemoveSocket(SocketSet *set, Socket *sock); +int CheckSockets(SocketSet *set, unsigned int timeout); + +// Packet API +void PacketSend(Packet *packet); +void PacketReceive(Packet *packet); +void PacketWrite8(Packet *packet, uint16_t value); +void PacketWrite16(Packet *packet, uint16_t value); +void PacketWrite32(Packet *packet, uint32_t value); +void PacketWrite64(Packet *packet, uint64_t value); +uint16_t PacketRead8(Packet *packet); +uint16_t PacketRead16(Packet *packet); +uint32_t PacketRead32(Packet *packet); +uint64_t PacketRead64(Packet *packet); + +#ifdef __cplusplus +} +#endif + +#endif // RNET_H + +/*********************************************************************************** +* +* RNET IMPLEMENTATION +* +************************************************************************************/ + +#if defined(RNET_IMPLEMENTATION) + +#include // Required for: assert() +#include // Required for: FILE, fopen(), fclose(), fread() +#include // Required for: malloc(), free() +#include // Required for: strcmp(), strncmp() + +#define NET_DEBUG_ENABLED 1 + +#if defined(SUPPORT_TRACELOG) + #define TRACELOG(level, ...) TraceLog(level, __VA_ARGS__) + + #if defined(SUPPORT_TRACELOG_DEBUG) + #define TRACELOGD(...) TraceLog(LOG_DEBUG, __VA_ARGS__) + #else + #define TRACELOGD(...) (void)0 + #endif +#else + #define TRACELOG(level, ...) (void)0 + #define TRACELOGD(...) (void)0 +#endif + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- + +typedef struct _SocketAddress +{ + struct sockaddr address; +} _SocketAddress; + +typedef struct _SocketAddressIPv4 +{ + struct sockaddr_in address; +} _SocketAddressIPv4; + +typedef struct _SocketAddressIPv6 +{ + struct sockaddr_in6 address; +} _SocketAddressIPv6; + +typedef struct _SocketAddressStorage +{ + struct sockaddr_storage address; +} _SocketAddressStorage; + +typedef struct _AddressInformation +{ + struct addrinfo addr; +} _AddressInformation; + +//---------------------------------------------------------------------------------- +// Local module Functions Declarations +//---------------------------------------------------------------------------------- +static void PrintSocket(struct sockaddr_storage *addr, const int family, const int socktype, const int protocol); +static const char *SocketAddressToString(struct sockaddr_storage *sockaddr); +static bool IsIPv4Address(const char *ip); +static bool IsIPv6Address(const char *ip); +static void *GetSocketPortPtr(struct sockaddr_storage *sa); +static void *GetSocketAddressPtr(struct sockaddr_storage *sa); +static bool IsSocketValid(Socket *sock); +static void SocketSetLastError(int err); +static int SocketGetLastError(); +static char *SocketGetLastErrorString(); +static char *SocketErrorCodeToString(int err); +static bool SocketSetDefaults(SocketConfig *config); +static bool InitSocket(Socket *sock, struct addrinfo *addr); +static bool CreateSocket(SocketConfig *config, SocketResult *outresult); +static bool SocketSetBlocking(Socket *sock); +static bool SocketSetNonBlocking(Socket *sock); +static bool SocketSetOptions(SocketConfig *config, Socket *sock); +static void SocketSetHints(SocketConfig *config, struct addrinfo *hints); + +//---------------------------------------------------------------------------------- +// Local module Functions Definition +//---------------------------------------------------------------------------------- +// Print socket information +static void PrintSocket(struct sockaddr_storage *addr, const int family, const int socktype, const int protocol) +{ + switch (family) + { + case AF_UNSPEC: TRACELOG(LOG_DEBUG, "\tFamily: Unspecified"); break; + case AF_INET: + { + TRACELOG(LOG_DEBUG, "\tFamily: AF_INET (IPv4)"); + TRACELOG(LOG_INFO, "\t- IPv4 address %s", SocketAddressToString(addr)); + } break; + case AF_INET6: + { + TRACELOG(LOG_DEBUG, "\tFamily: AF_INET6 (IPv6)"); + TRACELOG(LOG_INFO, "\t- IPv6 address %s", SocketAddressToString(addr)); + } break; + case AF_NETBIOS: + { + TRACELOG(LOG_DEBUG, "\tFamily: AF_NETBIOS (NetBIOS)"); + } break; + default: TRACELOG(LOG_DEBUG, "\tFamily: Other %ld", family); break; + } + + TRACELOG(LOG_DEBUG, "\tSocket type:"); + switch (socktype) + { + case 0: TRACELOG(LOG_DEBUG, "\t- Unspecified"); break; + case SOCK_STREAM: TRACELOG(LOG_DEBUG, "\t- SOCK_STREAM (stream)"); break; + case SOCK_DGRAM: TRACELOG(LOG_DEBUG, "\t- SOCK_DGRAM (datagram)"); break; + case SOCK_RAW: TRACELOG(LOG_DEBUG, "\t- SOCK_RAW (raw)"); break; + case SOCK_RDM: TRACELOG(LOG_DEBUG, "\t- SOCK_RDM (reliable message datagram)"); break; + case SOCK_SEQPACKET: TRACELOG(LOG_DEBUG, "\t- SOCK_SEQPACKET (pseudo-stream packet)"); break; + default: TRACELOG(LOG_DEBUG, "\t- Other %ld", socktype); break; + } + + TRACELOG(LOG_DEBUG, "\tProtocol:"); + switch (protocol) + { + case 0: TRACELOG(LOG_DEBUG, "\t- Unspecified"); break; + case IPPROTO_TCP: TRACELOG(LOG_DEBUG, "\t- IPPROTO_TCP (TCP)"); break; + case IPPROTO_UDP: TRACELOG(LOG_DEBUG, "\t- IPPROTO_UDP (UDP)"); break; + default: TRACELOG(LOG_DEBUG, "\t- Other %ld", protocol); break; + } +} + +// Convert network ordered socket address to human readable string (127.0.0.1) +static const char *SocketAddressToString(struct sockaddr_storage *sockaddr) +{ + //static const char* ipv6[INET6_ADDRSTRLEN]; + assert(sockaddr != NULL); + assert(sockaddr->ss_family == AF_INET || sockaddr->ss_family == AF_INET6); + + switch (sockaddr->ss_family) + { + case AF_INET: + { + //struct sockaddr_in *s = ((struct sockaddr_in *)sockaddr); + //return inet_ntop(AF_INET, &s->sin_addr, ipv6, INET_ADDRSTRLEN); // TODO. + } + break; + case AF_INET6: + { + //struct sockaddr_in6 *s = ((struct sockaddr_in6 *)sockaddr); + //return inet_ntop(AF_INET6, &s->sin6_addr, ipv6, INET6_ADDRSTRLEN); // TODO. + } + break; + } + + return NULL; +} + +// Check if the null terminated string ip is a valid IPv4 address +static bool IsIPv4Address(const char *ip) +{ + /* + struct sockaddr_in sa; + int result = inet_pton(AF_INET, ip, &(sa.sin_addr)); // TODO. + return (result != 0); + */ + return false; +} + +// Check if the null terminated string ip is a valid IPv6 address +static bool IsIPv6Address(const char *ip) +{ + /* + struct sockaddr_in6 sa; + int result = inet_pton(AF_INET6, ip, &(sa.sin6_addr)); // TODO. + return result != 0; + */ + return false; +} + +// Return a pointer to the port from the correct address family (IPv4, or IPv6) +static void *GetSocketPortPtr(struct sockaddr_storage *sa) +{ + if (sa->ss_family == AF_INET) + { + return &(((struct sockaddr_in *)sa)->sin_port); + } + + return &(((struct sockaddr_in6 *)sa)->sin6_port); +} + +// Return a pointer to the address from the correct address family (IPv4, or IPv6) +static void *GetSocketAddressPtr(struct sockaddr_storage *sa) +{ + if (sa->ss_family == AF_INET) + { + return &(((struct sockaddr_in *)sa)->sin_addr); + } + + return &(((struct sockaddr_in6 *)sa)->sin6_addr); +} + +// Is the socket in a valid state? +static bool IsSocketValid(Socket *sock) +{ + if (sock != NULL) + { + return (sock->channel != INVALID_SOCKET); + } + + return false; +} + +// Sets the error code that can be retrieved through the WSAGetLastError function. +static void SocketSetLastError(int err) +{ +#if defined(_WIN32) + WSASetLastError(err); +#else + errno = err; +#endif +} + +// Returns the error status for the last Sockets operation that failed +static int SocketGetLastError(void) +{ +#if defined(_WIN32) + return WSAGetLastError(); +#else + return errno; +#endif +} + +// Returns a human-readable string representing the last error message +static char *SocketGetLastErrorString(void) +{ + return SocketErrorCodeToString(SocketGetLastError()); +} + +// Returns a human-readable string representing the error message (err) +static char *SocketErrorCodeToString(int err) +{ +#if defined(_WIN32) + static char gaiStrErrorBuffer[GAI_STRERROR_BUFFER_SIZE]; + TRACELOG(LOG_INFO, gaiStrErrorBuffer, "%s", gai_strerror(err)); + return gaiStrErrorBuffer; +#else + return gai_strerror(err); +#endif +} + +// Set the defaults in the supplied SocketConfig if they're not already set +static bool SocketSetDefaults(SocketConfig *config) +{ + if (config->backlog_size == 0) config->backlog_size = SOCKET_MAX_QUEUE_SIZE; + + return true; +} + +// Create the socket channel +static bool InitSocket(Socket *sckt, struct addrinfo *address) +{ + switch (sckt->type) + { + case SOCKET_TCP: + { + if (address->ai_family == AF_INET) sckt->channel = socket(AF_INET, SOCK_STREAM, 0); + else sckt->channel = socket(AF_INET6, SOCK_STREAM, 0); + } break; + case SOCKET_UDP: + { + if (address->ai_family == AF_INET) sckt->channel = socket(AF_INET, SOCK_DGRAM, 0); + else sckt->channel = socket(AF_INET6, SOCK_DGRAM, 0); + } break; + default: TRACELOG(LOG_WARNING, "Invalid socket type specified."); break; + } + + return IsSocketValid(sckt); +} + +// CreateSocket() - Interally called by CreateSocket() +// +// This here is the bread and butter of the socket API, This function will +// attempt to open a socket, bind and listen to it based on the config passed in +// +// SocketConfig* config - Configuration for which socket to open +// SocketResult* result - The results of this function (if any, including errors) +// +// e.g. +// SocketConfig server_config = { SocketConfig client_config = { +// .host = "127.0.0.1", .host = "127.0.0.1", +// .port = 8080, .port = 8080, +// .server = true, }; +// .nonblocking = true, +// }; +// SocketResult server_res; SocketResult client_res; +static bool CreateSocket(SocketConfig *config, SocketResult *outresult) +{ + bool success = true; + int addrstatus; + struct addrinfo hints; // Address flags (IPV4, IPV6, UDP?) + struct addrinfo *res; // A pointer to the resulting address list + + outresult->socket->channel = INVALID_SOCKET; + outresult->status = RESULT_FAILURE; + + // Set the socket type + outresult->socket->type = config->type; + + // Set the hints based on information in the config + // + // AI_CANONNAME Causes the ai_canonname of the result to the filled out with the host's canonical (real) name. + // AI_PASSIVE: Causes the result's IP address to be filled out with INADDR_ANY (IPv4)or in6addr_any (IPv6); + // Note: This causes a subsequent call to bind() to auto-fill the IP address + // of the struct sockaddr with the address of the current host. + // + SocketSetHints(config, &hints); + + // Populate address information + addrstatus = getaddrinfo(config->host, // e.g. "www.example.com" or IP (Can be null if AI_PASSIVE flag is set + config->port, // e.g. "http" or port number + &hints, // e.g. SOCK_STREAM/SOCK_DGRAM + &res // The struct to populate + ); + + // Did we succeed? + if (addrstatus != 0) + { + outresult->socket->status = SocketGetLastError(); + TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(outresult->socket->status)); + SocketSetLastError(0); + TRACELOG(LOG_WARNING, "Failed to get resolve host %s:%s: %s", config->host, config->port, SocketGetLastErrorString()); + + return (success = false); + } + else + { + char hoststr[NI_MAXHOST]; + char portstr[NI_MAXSERV]; + //socklen_t client_len = sizeof(struct sockaddr_storage); + //int rc = getnameinfo((struct sockaddr *)res->ai_addr, client_len, hoststr, sizeof(hoststr), portstr, sizeof(portstr), NI_NUMERICHOST | NI_NUMERICSERV); + TRACELOG(LOG_INFO, "Successfully resolved host %s:%s", hoststr, portstr); + } + + // Walk the address information linked-list + struct addrinfo *it; + for (it = res; it != NULL; it = it->ai_next) + { + // Initialise the socket + if (!InitSocket(outresult->socket, it)) + { + outresult->socket->status = SocketGetLastError(); + TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(outresult->socket->status)); + SocketSetLastError(0); + continue; + } + + // Set socket options + if (!SocketSetOptions(config, outresult->socket)) + { + outresult->socket->status = SocketGetLastError(); + TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(outresult->socket->status)); + SocketSetLastError(0); + freeaddrinfo(res); + + return (success = false); + } + } + + if (!IsSocketValid(outresult->socket)) + { + outresult->socket->status = SocketGetLastError(); + TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(outresult->status)); + SocketSetLastError(0); + freeaddrinfo(res); + + return (success = false); + } + + if (success) + { + outresult->status = RESULT_SUCCESS; + outresult->socket->ready = 0; + outresult->socket->status = 0; + + if (!(config->type == SOCKET_UDP)) outresult->socket->isServer = config->server; + + switch (res->ai_addr->sa_family) + { + case AF_INET: + { + outresult->socket->addripv4 = (struct _SocketAddressIPv4 *)RNET_MALLOC(sizeof(*outresult->socket->addripv4)); + + if (outresult->socket->addripv4 != NULL) + { + memset(outresult->socket->addripv4, 0, sizeof(*outresult->socket->addripv4)); + + if (outresult->socket->addripv4 != NULL) + { + memcpy(&outresult->socket->addripv4->address, (struct sockaddr_in *)res->ai_addr, sizeof(struct sockaddr_in)); + + outresult->socket->isIPv6 = false; + char hoststr[NI_MAXHOST]; + char portstr[NI_MAXSERV]; + + socklen_t client_len = sizeof(struct sockaddr_storage); + getnameinfo((struct sockaddr *)&outresult->socket->addripv4->address, client_len, hoststr, sizeof(hoststr), portstr, sizeof(portstr), NI_NUMERICHOST | NI_NUMERICSERV); + + TRACELOG(LOG_INFO, "Socket address set to %s:%s", hoststr, portstr); + } + } + } break; + case AF_INET6: + { + outresult->socket->addripv6 = (struct _SocketAddressIPv6 *)RNET_MALLOC( + sizeof(*outresult->socket->addripv6)); + if (outresult->socket->addripv6 != NULL) + { + memset(outresult->socket->addripv6, 0, + sizeof(*outresult->socket->addripv6)); + if (outresult->socket->addripv6 != NULL) + { + memcpy(&outresult->socket->addripv6->address, + (struct sockaddr_in6 *)res->ai_addr, sizeof(struct sockaddr_in6)); + outresult->socket->isIPv6 = true; + char hoststr[NI_MAXHOST]; + char portstr[NI_MAXSERV]; + socklen_t client_len = sizeof(struct sockaddr_storage); + getnameinfo( + (struct sockaddr *)&outresult->socket->addripv6->address, client_len, hoststr, sizeof(hoststr), portstr, sizeof(portstr), NI_NUMERICHOST | NI_NUMERICSERV); + TRACELOG(LOG_INFO, "Socket address set to %s:%s", hoststr, portstr); + } + } + } break; + default: break; + } + } + + freeaddrinfo(res); + return success; +} + +// Set the state of the Socket sock to blocking +static bool SocketSetBlocking(Socket *sock) +{ + bool ret = true; +#if defined(_WIN32) + unsigned long mode = 0; + ret = ioctlsocket(sock->channel, FIONBIO, &mode); +#else + const int flags = fcntl(sock->channel, F_GETFL, 0); + if (!(flags & O_NONBLOCK)) + { + TRACELOG(LOG_DEBUG, "Socket was already in blocking mode"); + return ret; + } + + ret = (0 == fcntl(sock->channel, F_SETFL, (flags ^ O_NONBLOCK))); +#endif + return ret; +} + +// Set the state of the Socket sock to non-blocking +static bool SocketSetNonBlocking(Socket *sock) +{ + bool ret = true; +#if defined(_WIN32) + unsigned long mode = 1; + ret = ioctlsocket(sock->channel, FIONBIO, &mode); +#else + const int flags = fcntl(sock->channel, F_GETFL, 0); + + if ((flags & O_NONBLOCK)) + { + TRACELOG(LOG_DEBUG, "Socket was already in non-blocking mode"); + return ret; + } + + ret = (0 == fcntl(sock->channel, F_SETFL, (flags | O_NONBLOCK))); +#endif + return ret; +} + +// Set options specified in SocketConfig to Socket sock +static bool SocketSetOptions(SocketConfig *config, Socket *sock) +{ + for (int i = 0; i < SOCKET_MAX_SOCK_OPTS; i++) + { + SocketOpt *opt = &config->sockopts[i]; + + if (opt->id == 0) break; + + if (setsockopt(sock->channel, SOL_SOCKET, opt->id, opt->value, opt->valueLen) < 0) return false; + } + + return true; +} + +// Set "hints" in an addrinfo struct, to be passed to getaddrinfo. +static void SocketSetHints(SocketConfig *config, struct addrinfo *hints) +{ + if (config == NULL || hints == NULL) return; + + memset(hints, 0, sizeof(*hints)); + + // Check if the ip supplied in the config is a valid ipv4 ip ipv6 address + if (IsIPv4Address(config->host)) + { + hints->ai_family = AF_INET; + hints->ai_flags |= AI_NUMERICHOST; + } + else + { + if (IsIPv6Address(config->host)) + { + hints->ai_family = AF_INET6; + hints->ai_flags |= AI_NUMERICHOST; + } + else hints->ai_family = AF_UNSPEC; + } + + if (config->type == SOCKET_UDP) hints->ai_socktype = SOCK_DGRAM; + else hints->ai_socktype = SOCK_STREAM; + + + // Set passive unless UDP client + if (!(config->type == SOCKET_UDP) || config->server) hints->ai_flags = AI_PASSIVE; +} + +//---------------------------------------------------------------------------------- +// Module implementation +//---------------------------------------------------------------------------------- + +// Initialise the network (requires for windows platforms only) +bool InitNetworkDevice(void) +{ +#if defined(_WIN32) + WORD wVersionRequested; + WSADATA wsaData; + + wVersionRequested = MAKEWORD(2, 2); + int err = WSAStartup(wVersionRequested, &wsaData); + + if (err != 0) + { + TRACELOG(LOG_WARNING, "WinSock failed to initialise."); + return false; + } + else TRACELOG(LOG_INFO, "WinSock initialised."); + + if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) + { + TRACELOG(LOG_WARNING, "WinSock failed to initialise."); + WSACleanup(); + return false; + } + + return true; +#else + return true; +#endif +} + +// Cleanup, and close the network +void CloseNetworkDevice(void) +{ +#if defined(_WIN32) + WSACleanup(); +#endif +} + +// Protocol-independent name resolution from an address to an ANSI host name +// and from a port number to the ANSI service name. +// +// The flags parameter can be used to customize processing of the getnameinfo function +// +// The following flags are available: +// +// NAME_INFO_DEFAULT 0x00 // No flags set +// NAME_INFO_NOFQDN 0x01 // Only return nodename portion for local hosts +// NAME_INFO_NUMERICHOST 0x02 // Return numeric form of the host's address +// NAME_INFO_NAMEREQD 0x04 // Error if the host's name not in DNS +// NAME_INFO_NUMERICSERV 0x08 // Return numeric form of the service (port #) +// NAME_INFO_DGRAM 0x10 // Service is a datagram service +void ResolveIP(const char *ip, const char *port, int flags, char *host, char *serv) +{ + // Variables + int status; // Status value to return (0) is success + struct addrinfo hints; // Address flags (IPV4, IPV6, UDP?) + struct addrinfo *res; // A pointer to the resulting address list + + // Set the hints + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; // Either IPv4 or IPv6 (AF_INET, AF_INET6) + hints.ai_protocol = 0; // Automatically select correct protocol (IPPROTO_TCP), (IPPROTO_UDP) + + // Populate address information + status = getaddrinfo(ip, // e.g. "www.example.com" or IP + port, // e.g. "http" or port number + &hints, // e.g. SOCK_STREAM/SOCK_DGRAM + &res // The struct to populate + ); + + // Did we succeed? + if (status != 0) TRACELOG(LOG_WARNING, "Failed to get resolve host %s:%s: %s", ip, port, gai_strerror(errno)); + else TRACELOG(LOG_DEBUG, "Resolving... %s::%s", ip, port); + + // Attempt to resolve network byte order ip to hostname + switch (res->ai_family) + { + case AF_INET: + { + status = getnameinfo(&*((struct sockaddr *)res->ai_addr), + sizeof(*((struct sockaddr_in *)res->ai_addr)), + host, NI_MAXHOST, serv, NI_MAXSERV, flags); + } break; + case AF_INET6: + { + /* + status = getnameinfo(&*((struct sockaddr_in6 *)res->ai_addr), // TODO. + sizeof(*((struct sockaddr_in6 *)res->ai_addr)), + host, NI_MAXHOST, serv, NI_MAXSERV, flags); + */ + } break; + default: break; + } + + if (status != 0) TRACELOG(LOG_WARNING, "Failed to resolve ip %s: %s", ip, SocketGetLastErrorString()); + else TRACELOG(LOG_DEBUG, "Successfully resolved %s::%s to %s", ip, port, host); + + // Free the pointer to the data returned by addrinfo + freeaddrinfo(res); +} + +// Protocol-independent translation from an ANSI host name to an address +// +// e.g. +// const char* address = "127.0.0.1" (local address) +// const char* port = "80" +// +// Parameters: +// const char* address - A pointer to a NULL-terminated ANSI string that contains a host (node) name or a numeric host address string. +// const char* service - A pointer to a NULL-terminated ANSI string that contains either a service name or port number represented as a string. +// +// Returns: +// The total amount of addresses found, -1 on error +// +int ResolveHost(const char *address, const char *service, int addressType, int flags, AddressInformation *outAddr) +{ + // Variables + int status; // Status value to return (0) is success + struct addrinfo hints; // Address flags (IPV4, IPV6, UDP?) + struct addrinfo *res; // will point to the results + struct addrinfo *iterator; + assert(((address != NULL || address != 0) || (service != NULL || service != 0))); + assert(((addressType == AF_INET) || (addressType == AF_INET6) || (addressType == AF_UNSPEC))); + + // Set the hints + memset(&hints, 0, sizeof hints); + hints.ai_family = addressType; // Either IPv4 or IPv6 (ADDRESS_TYPE_IPV4, ADDRESS_TYPE_IPV6) + hints.ai_protocol = 0; // Automatically select correct protocol (IPPROTO_TCP), (IPPROTO_UDP) + hints.ai_flags = flags; + assert((hints.ai_addrlen == 0) || (hints.ai_addrlen == 0)); + assert((hints.ai_canonname == 0) || (hints.ai_canonname == 0)); + assert((hints.ai_addr == 0) || (hints.ai_addr == 0)); + assert((hints.ai_next == 0) || (hints.ai_next == 0)); + + // When the address is NULL, populate the IP for me + if (address == NULL) + { + if ((hints.ai_flags & AI_PASSIVE) == 0) hints.ai_flags |= AI_PASSIVE; + } + + TRACELOG(LOG_INFO, "Resolving host..."); + + // Populate address information + status = getaddrinfo(address, // e.g. "www.example.com" or IP + service, // e.g. "http" or port number + &hints, // e.g. SOCK_STREAM/SOCK_DGRAM + &res // The struct to populate + ); + + // Did we succeed? + if (status != 0) + { + int error = SocketGetLastError(); + SocketSetLastError(0); + TRACELOG(LOG_WARNING, "Failed to get resolve host: %s", SocketErrorCodeToString(error)); + return -1; + } + else TRACELOG(LOG_INFO, "Successfully resolved host %s:%s", address, service); + + // Calculate the size of the address information list + int size = 0; + for (iterator = res; iterator != NULL; iterator = iterator->ai_next) size++; + + // Validate the size is > 0, otherwise return + if (size <= 0) + { + TRACELOG(LOG_WARNING, "Error, no addresses found."); + return -1; + } + + // If not address list was allocated, allocate it dynamically with the known address size + if (outAddr == NULL) outAddr = (AddressInformation *)RNET_MALLOC(size * sizeof(AddressInformation)); + + // Dynamically allocate an array of address information structs + if (outAddr != NULL) + { + int i; + for (i = 0; i < size; ++i) + { + outAddr[i] = LoadAddress(); + if (outAddr[i] == NULL) + { + break; + } + } + + outAddr[i] = NULL; + if (i != size) outAddr = NULL; + } + else + { + TRACELOG(LOG_WARNING, "Error, failed to dynamically allocate memory for the address list"); + return -1; + } + + // Copy all the address information from res into outAddrList + int i = 0; + for (iterator = res; iterator != NULL; iterator = iterator->ai_next) + { + if (i < size) + { + outAddr[i]->addr.ai_flags = iterator->ai_flags; + outAddr[i]->addr.ai_family = iterator->ai_family; + outAddr[i]->addr.ai_socktype = iterator->ai_socktype; + outAddr[i]->addr.ai_protocol = iterator->ai_protocol; + outAddr[i]->addr.ai_addrlen = iterator->ai_addrlen; + *outAddr[i]->addr.ai_addr = *iterator->ai_addr; +#if NET_DEBUG_ENABLED + TRACELOG(LOG_DEBUG, "GetAddressInformation"); + TRACELOG(LOG_DEBUG, "\tFlags: 0x%x", iterator->ai_flags); + //PrintSocket(outAddr[i]->addr.ai_addr, outAddr[i]->addr.ai_family, outAddr[i]->addr.ai_socktype, outAddr[i]->addr.ai_protocol); + TRACELOG(LOG_DEBUG, "Length of this sockaddr: %d", outAddr[i]->addr.ai_addrlen); + TRACELOG(LOG_DEBUG, "Canonical name: %s", iterator->ai_canonname); +#endif + i++; + } + } + + // Free the pointer to the data returned by addrinfo + freeaddrinfo(res); + + // Return the total count of addresses found + return size; +} + +// This here is the bread and butter of the socket API, This function will +// attempt to open a socket, bind and listen to it based on the config passed in +// +// SocketConfig* config - Configuration for which socket to open +// SocketResult* result - The results of this function (if any, including errors) +// +// e.g. +// SocketConfig server_config = { SocketConfig client_config = { +// .host = "127.0.0.1", .host = "127.0.0.1", +// .port = 8080, .port = 8080, +// .server = true, }; +// .nonblocking = true, +// }; +// SocketResult server_res; SocketResult client_res; +bool SocketCreate(SocketConfig *config, SocketResult *result) +{ + // Socket creation result + bool success = true; + + // Make sure we've not received a null config or result pointer + if (config == NULL || result == NULL) return (success = false); + + // Set the defaults based on the config + if (!SocketSetDefaults(config)) + { + TRACELOG(LOG_WARNING, "Configuration Error."); + success = false; + } + else + { + // Create the socket + if (CreateSocket(config, result)) + { + if (config->nonblocking) SocketSetNonBlocking(result->socket); + else SocketSetBlocking(result->socket); + } + else success = false; + } + + return success; +} + +// Bind a socket to a local address +// Note: The bind function is required on an unconnected socket before subsequent calls to the listen function. +bool SocketBind(SocketConfig *config, SocketResult *result) +{ + bool success = false; + result->status = RESULT_FAILURE; + struct sockaddr_storage *sock_addr = NULL; + + // Don't bind to a socket that isn't configured as a server + if (!IsSocketValid(result->socket) || !config->server) + { + TRACELOG(LOG_WARNING, Cannot bind to socket marked as \"Client\" in SocketConfig."); + success = false; + } + else + { + if (result->socket->isIPv6) sock_addr = (struct sockaddr_storage *)&result->socket->addripv6->address; + else sock_addr = (struct sockaddr_storage *)&result->socket->addripv4->address; + + if (sock_addr != NULL) + { + if (bind(result->socket->channel, (struct sockaddr *)sock_addr, sizeof(*sock_addr)) != SOCKET_ERROR) + { + TRACELOG(LOG_INFO, "Successfully bound socket."); + success = true; + } + else + { + result->socket->status = SocketGetLastError(); + TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(result->socket->status)); + SocketSetLastError(0); + success = false; + } + } + } + + // Was the bind a success? + if (success) + { + result->status = RESULT_SUCCESS; + result->socket->ready = 0; + result->socket->status = 0; + socklen_t sock_len = sizeof(*sock_addr); + + if (getsockname(result->socket->channel, (struct sockaddr *)sock_addr, &sock_len) < 0) + { + TRACELOG(LOG_WARNING, "Couldn't get socket address"); + } + else + { + struct sockaddr_in *s = (struct sockaddr_in *)sock_addr; + // result->socket->address.host = s->sin_addr.s_addr; + // result->socket->address.port = s->sin_port; + + result->socket->addripv4 = (struct _SocketAddressIPv4 *)RNET_MALLOC(sizeof(*result->socket->addripv4)); + + if (result->socket->addripv4 != NULL) memset(result->socket->addripv4, 0, sizeof(*result->socket->addripv4)); + + memcpy(&result->socket->addripv4->address, (struct sockaddr_in *)&s->sin_addr, sizeof(struct sockaddr_in)); + } + } + return success; +} + +// Listens (and queues) incoming connections requests for a bound port. +bool SocketListen(SocketConfig *config, SocketResult *result) +{ + bool success = false; + result->status = RESULT_FAILURE; + + // Don't bind to a socket that isn't configured as a server + if (!IsSocketValid(result->socket) || !config->server) + { + TRACELOG(LOG_WARNING, "Cannot listen on socket marked as \"Client\" in SocketConfig."); + success = false; + } + else + { + // Don't listen on UDP sockets + if (!(config->type == SOCKET_UDP)) + { + if (listen(result->socket->channel, config->backlog_size) != SOCKET_ERROR) + { + TRACELOG(LOG_INFO, "Started listening on socket..."); + success = true; + } + else + { + success = false; + result->socket->status = SocketGetLastError(); + TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(result->socket->status)); + SocketSetLastError(0); + } + } + else + { + TRACELOG(LOG_WARNING, "Cannot listen on socket marked as \"UDP\" (datagram) in SocketConfig."); + success = false; + } + } + + // Was the listen a success? + if (success) + { + result->status = RESULT_SUCCESS; + result->socket->ready = 0; + result->socket->status = 0; + } + + return success; +} + +// Connect the socket to the destination specified by "host" and "port" in SocketConfig +bool SocketConnect(SocketConfig *config, SocketResult *result) +{ + bool success = true; + result->status = RESULT_FAILURE; + + // Only bind to sockets marked as server + if (config->server) + { + TRACELOG(LOG_WARNING, "Cannot connect to socket marked as \"Server\" in SocketConfig."); + success = false; + } + else + { + if (IsIPv4Address(config->host)) + { + struct sockaddr_in ip4addr; + ip4addr.sin_family = AF_INET; + unsigned long hport; + hport = strtoul(config->port, NULL, 0); + ip4addr.sin_port = (unsigned short)(hport); + + // TODO: Changed the code to avoid the usage of inet_pton and inet_ntop replacing them with getnameinfo (that should have a better support on windows). + + //inet_pton(AF_INET, config->host, &ip4addr.sin_addr); + int connect_result = connect(result->socket->channel, (struct sockaddr *)&ip4addr, sizeof(ip4addr)); + + if (connect_result == SOCKET_ERROR) + { + result->socket->status = SocketGetLastError(); + SocketSetLastError(0); + + switch (result->socket->status) + { + case WSAEWOULDBLOCK: success = true; break; + default: + { + TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(result->socket->status)); + success = false; + } break; + } + } + else + { + TRACELOG(LOG_INFO, "Successfully connected to socket."); + success = true; + } + } + else + { + if (IsIPv6Address(config->host)) + { + struct sockaddr_in6 ip6addr; + ip6addr.sin6_family = AF_INET6; + unsigned long hport; + hport = strtoul(config->port, NULL, 0); + ip6addr.sin6_port = htons((unsigned short)hport); + //inet_pton(AF_INET6, config->host, &ip6addr.sin6_addr); // TODO. + int connect_result = connect(result->socket->channel, (struct sockaddr *)&ip6addr, sizeof(ip6addr)); + + if (connect_result == SOCKET_ERROR) + { + result->socket->status = SocketGetLastError(); + SocketSetLastError(0); + + switch (result->socket->status) + { + case WSAEWOULDBLOCK: success = true; break; + default: + { + TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(result->socket->status)); + success = false; + } break; + } + } + else + { + TRACELOG(LOG_INFO, "Successfully connected to socket."); + success = true; + } + } + } + } + + if (success) + { + result->status = RESULT_SUCCESS; + result->socket->ready = 0; + result->socket->status = 0; + } + + return success; +} + +// Closes an existing socket +// +// SocketChannel socket - The id of the socket to close +void SocketClose(Socket *sock) +{ + if (sock != NULL) + { + if (sock->channel != INVALID_SOCKET) closesocket(sock->channel); + } +} + +// Returns the sockaddress for a specific socket in a generic storage struct +SocketAddressStorage SocketGetPeerAddress(Socket *sock) +{ + // TODO. + /* + if (sock->isServer) return NULL; + if (sock->isIPv6) return sock->addripv6; + else return sock->addripv4; + */ + + return NULL; +} + +// Return the address-type appropriate host portion of a socket address +const char *GetSocketAddressHost(SocketAddressStorage storage) +{ + assert(storage->address.ss_family == AF_INET || storage->address.ss_family == AF_INET6); + return SocketAddressToString((struct sockaddr_storage *)storage); +} + +// Return the address-type appropriate port(service) portion of a socket address +short GetSocketAddressPort(SocketAddressStorage storage) +{ + //return ntohs(GetSocketPortPtr(storage)); // TODO. + + return 0; +} + +// The accept function permits an incoming connection attempt on a socket. +// +// SocketChannel listener - The socket to listen for incoming connections on (i.e. server) +// SocketResult* out - The result of this function (if any, including errors) +// +// e.g. +// +// SocketResult connection; +// bool connected = false; +// if (!connected) +// { +// if (SocketAccept(server_res.socket.channel, &connection)) +// { +// connected = true; +// } +// } +Socket *SocketAccept(Socket *server, SocketConfig *config) +{ + if (!server->isServer || server->type == SOCKET_UDP) return NULL; + + struct sockaddr_storage sock_addr; + socklen_t sock_alen; + Socket *sock = LoadSocket(); + server->ready = 0; + sock_alen = sizeof(sock_addr); + sock->channel = accept(server->channel, (struct sockaddr *)&sock_addr, &sock_alen); + + if (sock->channel == INVALID_SOCKET) + { + sock->status = SocketGetLastError(); + TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(sock->status)); + SocketSetLastError(0); + SocketClose(sock); + + return NULL; + } + + (config->nonblocking) ? SocketSetNonBlocking(sock) : SocketSetBlocking(sock); + sock->isServer = false; + sock->ready = 0; + sock->type = server->type; + + switch (sock_addr.ss_family) + { + case AF_INET: + { + struct sockaddr_in *s = ((struct sockaddr_in *)&sock_addr); + sock->addripv4 = (struct _SocketAddressIPv4 *)RNET_MALLOC(sizeof(*sock->addripv4)); + + if (sock->addripv4 != NULL) + { + memset(sock->addripv4, 0, sizeof(*sock->addripv4)); + memcpy(&sock->addripv4->address, (struct sockaddr_in *)&s->sin_addr, sizeof(struct sockaddr_in)); + TRACELOG(LOG_INFO, "Server: Got connection from %s::%hu", SocketAddressToString((struct sockaddr_storage *)s), ntohs(sock->addripv4->address.sin_port)); + } + } break; + case AF_INET6: + { + struct sockaddr_in6 *s = ((struct sockaddr_in6 *)&sock_addr); + sock->addripv6 = (struct _SocketAddressIPv6 *)RNET_MALLOC(sizeof(*sock->addripv6)); + + if (sock->addripv6 != NULL) + { + memset(sock->addripv6, 0, sizeof(*sock->addripv6)); + memcpy(&sock->addripv6->address, (struct sockaddr_in6 *)&s->sin6_addr, sizeof(struct sockaddr_in6)); + TRACELOG(LOG_INFO, "Server: Got connection from %s::%hu", SocketAddressToString((struct sockaddr_storage *)s), ntohs(sock->addripv6->address.sin6_port)); + } + } break; + } + + return sock; +} + +// Verify that the channel is in the valid range +static int ValidChannel(int channel) +{ + if ((channel < 0) || (channel >= SOCKET_MAX_UDPCHANNELS)) + { + TRACELOG(LOG_WARNING, "Invalid channel"); + return 0; + } + + return 1; +} + +// Set the socket channel +int SocketSetChannel(Socket *socket, int channel, const IPAddress *address) +{ + struct UDPChannel *binding; + + if (socket == NULL) + { + TRACELOG(LOG_WARNING, "Passed a NULL socket"); + return (-1); + } + + if (channel == -1) + { + for (channel = 0; channel < SOCKET_MAX_UDPCHANNELS; ++channel) + { + binding = &socket->binding[channel]; + if (binding->numbound < SOCKET_MAX_UDPADDRESSES) break; + } + } + else + { + if (!ValidChannel(channel)) return (-1); + + binding = &socket->binding[channel]; + } + + if (binding->numbound == SOCKET_MAX_UDPADDRESSES) + { + TRACELOG(LOG_WARNING, "No room for new addresses"); + return (-1); + } + + binding->address[binding->numbound++] = *address; + + return (channel); +} + +// Remove the socket channel +void SocketUnsetChannel(Socket *socket, int channel) +{ + if ((channel >= 0) && (channel < SOCKET_MAX_UDPCHANNELS)) socket->binding[channel].numbound = 0; +} + +/* Allocate/free a single UDP packet 'size' bytes long. + The new packet is returned, or NULL if the function ran out of memory. + */ +SocketDataPacket *AllocPacket(int size) +{ + SocketDataPacket *packet = (SocketDataPacket *)RNET_MALLOC(sizeof(*packet)); + int error = 1; + + if (packet != NULL) + { + packet->maxlen = size; + packet->data = (uint8_t *)RNET_MALLOC(size); + if (packet->data != NULL) + { + error = 0; + } + } + + if (error) + { + FreePacket(packet); + packet = NULL; + } + + return (packet); +} + +int ResizePacket(SocketDataPacket *packet, int newsize) +{ + uint8_t *newdata = (uint8_t *)RNET_MALLOC(newsize); + + if (newdata != NULL) + { + RNET_FREE(packet->data); + packet->data = newdata; + packet->maxlen = newsize; + } + + return (packet->maxlen); +} + +void FreePacket(SocketDataPacket *packet) +{ + if (packet) + { + RNET_FREE(packet->data); + RNET_FREE(packet); + } +} + +// Allocate/Free a UDP packet vector (array of packets) of 'howmany' packets, each 'size' bytes long. +// A pointer to the packet array is returned, or NULL if the function ran out of memory. +SocketDataPacket **AllocPacketList(int howmany, int size) +{ + SocketDataPacket **packetV = (SocketDataPacket **)RNET_MALLOC((howmany + 1) * sizeof(*packetV)); + + if (packetV != NULL) + { + int i; + for (i = 0; i < howmany; ++i) + { + packetV[i] = AllocPacket(size); + if (packetV[i] == NULL) + { + break; + } + } + packetV[i] = NULL; + + if (i != howmany) + { + FreePacketList(packetV); + packetV = NULL; + } + } + + return (packetV); +} + +void FreePacketList(SocketDataPacket **packetV) +{ + if (packetV) + { + for (int i = 0; packetV[i]; ++i) FreePacket(packetV[i]); + RNET_FREE(packetV); + } +} + +// Send 'len' bytes of 'data' over the non-server socket 'sock' +int SocketSend(Socket *sock, const void *datap, int length) +{ + int sent = 0; + int left = length; + int status = -1; + int numsent = 0; + const unsigned char *data = (const unsigned char *)datap; + + // Server sockets are for accepting connections only + if (sock->isServer) + { + TRACELOG(LOG_WARNING, "Cannot send information on a server socket"); + return -1; + } + + // Which socket are we trying to send data on + switch (sock->type) + { + case SOCKET_TCP: + { + SocketSetLastError(0); + do + { + length = send(sock->channel, (const char *)data, left, 0); + if (length > 0) + { + sent += length; + left -= length; + data += length; + } + } while ((left > 0) && // While we still have bytes left to send + ((length > 0) || // The amount of bytes we actually sent is > 0 + (SocketGetLastError() == WSAEINTR)) // The socket was interupted + ); + + if (length == SOCKET_ERROR) + { + sock->status = SocketGetLastError(); + TRACELOG(LOG_DEBUG, "Socket Error: %s", SocketErrorCodeToString(sock->status)); + SocketSetLastError(0); + } + else TRACELOG(LOG_DEBUG, "Successfully sent \"%s\" (%d bytes)", datap, sent); + + return sent; + } break; + case SOCKET_UDP: + { + SocketSetLastError(0); + + if (sock->isIPv6) status = sendto(sock->channel, (const char *)data, left, 0, (struct sockaddr *)&sock->addripv6->address, sizeof(sock->addripv6->address)); + else status = sendto(sock->channel, (const char *)data, left, 0, (struct sockaddr *)&sock->addripv4->address, sizeof(sock->addripv4->address)); + + if (sent >= 0) + { + sock->status = 0; + ++numsent; + TRACELOG(LOG_DEBUG, "Successfully sent \"%s\" (%d bytes)", datap, status); + } + else + { + sock->status = SocketGetLastError(); + TRACELOG(LOG_DEBUG, "Socket Error: %s", SocketGetLastErrorString(sock->status)); + SocketSetLastError(0); + return 0; + } + + return numsent; + } break; + default: break; + } + + return -1; +} + +// Receive up to 'maxlen' bytes of data over the non-server socket 'sock', +// and store them in the buffer pointed to by 'data'. +// This function returns the actual amount of data received. If the return +// value is less than or equal to zero, then either the remote connection was +// closed, or an unknown socket error occurred. +int SocketReceive(Socket *sock, void *data, int maxlen) +{ + int len = 0; + int numrecv = 0; + int status = 0; + socklen_t sock_len; + struct sockaddr_storage sock_addr; + //char ip[INET6_ADDRSTRLEN]; + + // Server sockets are for accepting connections only + if (sock->isServer && (sock->type == SOCKET_TCP)) + { + sock->status = SocketGetLastError(); + TRACELOG(LOG_DEBUG, "Socket Error: %s", "Server sockets cannot be used to receive data"); + SocketSetLastError(0); + return 0; + } + + // Which socket are we trying to send data on + switch (sock->type) + { + case SOCKET_TCP: + { + SocketSetLastError(0); + do + { + len = recv(sock->channel, (char *)data, maxlen, 0); + } while (SocketGetLastError() == WSAEINTR); + + if (len > 0) + { + // Who sent the packet? + if (sock->type == SOCKET_UDP) + { + //TRACELOG(LOG_DEBUG, "Received data from: %s", inet_ntop(sock_addr.ss_family, GetSocketAddressPtr((struct sockaddr *)&sock_addr), ip, sizeof(ip))); + } + + ((unsigned char *)data)[len] = '\0'; // Add null terminating character to the end of the stream + TRACELOG(LOG_DEBUG, "Received \"%s\" (%d bytes)", data, len); + } + + sock->ready = 0; + return len; + } break; + case SOCKET_UDP: + { + SocketSetLastError(0); + sock_len = sizeof(sock_addr); + status = recvfrom(sock->channel, // The receving channel + data, // A pointer to the data buffer to fill + maxlen, // The max length of the data to fill + 0, // Flags + (struct sockaddr *)&sock_addr, // The address of the recevied data + &sock_len // The length of the received data address + ); + + if (status >= 0) ++numrecv; + else + { + sock->status = SocketGetLastError(); + + switch (sock->status) + { + case WSAEWOULDBLOCK: break; + default: TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(sock->status)); break; + } + + SocketSetLastError(0); + return 0; + } + + sock->ready = 0; + return numrecv; + } break; + default: break; + } + + return -1; +} + +// Does the socket have it's 'ready' flag set? +bool IsSocketReady(Socket *sock) +{ + return (sock != NULL) && (sock->ready); +} + +// Check if the socket is considered connected +bool IsSocketConnected(Socket *sock) +{ +#if defined(_WIN32) + FD_SET writefds; + FD_ZERO(&writefds); + FD_SET(sock->channel, &writefds); + struct timeval timeout; + timeout.tv_sec = 1; + timeout.tv_usec = 1000000000UL; + int total = select(0, NULL, &writefds, NULL, &timeout); + + if (total == -1) + { // Error + sock->status = SocketGetLastError(); + TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(sock->status)); + SocketSetLastError(0); + } + else if (total == 0) return false; // Timeout + else if (FD_ISSET(sock->channel, &writefds)) return true; + + return false; +#else + return true; +#endif +} + +// Allocate and return a SocketResult struct +SocketResult *LoadSocketResult(void) +{ + struct SocketResult *res = (struct SocketResult *)RNET_MALLOC(sizeof(*res)); + + if (res != NULL) + { + memset(res, 0, sizeof(*res)); + if ((res->socket = LoadSocket()) == NULL) + { + RNET_FREE(res); + res = NULL; + } + } + + return res; +} + +// Free an allocated SocketResult +void UnloadSocketResult(SocketResult **result) +{ + if (*result != NULL) + { + if ((*result)->socket != NULL) UnloadSocket(&((*result)->socket)); + + RNET_FREE(*result); + *result = NULL; + } +} + +// Allocate a Socket +Socket *LoadSocket(void) +{ + struct Socket *sock; + sock = (Socket *)RNET_MALLOC(sizeof(*sock)); + + if (sock != NULL) memset(sock, 0, sizeof(*sock)); + else + { + TRACELOG(LOG_WARNING, "Ran out of memory attempting to allocate a socket"); + SocketClose(sock); + RNET_FREE(sock); + sock = NULL; + } + + return sock; +} + +// Free an allocated Socket +void UnloadSocket(Socket **sock) +{ + if (*sock != NULL) + { + RNET_FREE(*sock); + *sock = NULL; + } +} + +// Allocate a SocketSet +SocketSet *LoadSocketSet(int max) +{ + struct SocketSet *set = (struct SocketSet *)RNET_MALLOC(sizeof(*set)); + + if (set != NULL) + { + set->numsockets = 0; + set->maxsockets = max; + set->sockets = (struct Socket **)RNET_MALLOC(max * sizeof(*set->sockets)); + if (set->sockets != NULL) + { + for (int i = 0; i < max; ++i) set->sockets[i] = NULL; + } + else + { + RNET_FREE(set); + set = NULL; + } + } + + return (set); +} + +// Free an allocated SocketSet +void UnloadSocketSet(SocketSet *set) +{ + if (set) + { + RNET_FREE(set->sockets); + RNET_FREE(set); + } +} + +// Add a Socket "sock" to the SocketSet "set" +int AddSocket(SocketSet *set, Socket *sock) +{ + if (sock != NULL) + { + if (set->numsockets == set->maxsockets) + { + TRACELOG(LOG_DEBUG, "Socket Error: %s", "SocketSet is full"); + SocketSetLastError(0); + return (-1); + } + set->sockets[set->numsockets++] = (struct Socket *)sock; + } + else + { + TRACELOG(LOG_DEBUG, "Socket Error: %s", "Socket was null"); + SocketSetLastError(0); + return (-1); + } + + return (set->numsockets); +} + +// Remove a Socket "sock" to the SocketSet "set" +int RemoveSocket(SocketSet *set, Socket *sock) +{ + if (sock != NULL) + { + int i = 0; + for (i = 0; i < set->numsockets; ++i) + { + if (set->sockets[i] == (struct Socket *)sock) break; + } + + if (i == set->numsockets) + { + TRACELOG(LOG_DEBUG, "Socket Error: %s", "Socket not found"); + SocketSetLastError(0); + return (-1); + } + + --set->numsockets; + for (; i < set->numsockets; ++i) set->sockets[i] = set->sockets[i + 1]; + } + + return (set->numsockets); +} + +// Check the sockets in the socket set for pending information +int CheckSockets(SocketSet *set, unsigned int timeout) +{ + int i; + SOCKET maxfd; + int retval; + struct timeval tv; + fd_set mask; + + /* Find the largest file descriptor */ + maxfd = 0; + for (i = set->numsockets - 1; i >= 0; --i) + { + if (set->sockets[i]->channel > maxfd) + { + maxfd = set->sockets[i]->channel; + } + } + + // Check the file descriptors for available data + do + { + SocketSetLastError(0); + + // Set up the mask of file descriptors + FD_ZERO(&mask); + for (i = set->numsockets - 1; i >= 0; --i) + { + FD_SET(set->sockets[i]->channel, &mask); + } // Set up the timeout + + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + + /* Look! */ + retval = select(maxfd + 1, &mask, NULL, NULL, &tv); + } while (SocketGetLastError() == WSAEINTR); + + // Mark all file descriptors ready that have data available + if (retval > 0) + { + for (i = set->numsockets - 1; i >= 0; --i) + { + if (FD_ISSET(set->sockets[i]->channel, &mask)) set->sockets[i]->ready = 1; + } + } + + return retval; +} + +// Allocate an AddressInformation +AddressInformation LoadAddress(void) +{ + AddressInformation addressInfo = NULL; + addressInfo = (AddressInformation)RNET_CALLOC(1, sizeof(*addressInfo)); + + if (addressInfo != NULL) + { + addressInfo->addr.ai_addr = (struct sockaddr *)RNET_CALLOC(1, sizeof(struct sockaddr)); + if (addressInfo->addr.ai_addr == NULL) TRACELOG(LOG_WARNING, "Failed to allocate memory for \"struct sockaddr\""); + } + else TRACELOG(LOG_WARNING, "Failed to allocate memory for \"struct AddressInformation\""); + + return addressInfo; +} + +// Free an AddressInformation struct +void UnloadAddress(AddressInformation *addressInfo) +{ + if (*addressInfo != NULL) + { + if ((*addressInfo)->addr.ai_addr != NULL) + { + RNET_FREE((*addressInfo)->addr.ai_addr); + (*addressInfo)->addr.ai_addr = NULL; + } + + RNET_FREE(*addressInfo); + *addressInfo = NULL; + } +} + +// Allocate a list of AddressInformation +AddressInformation *LoadAddressList(int size) +{ + AddressInformation *addr; + addr = (AddressInformation *)RNET_MALLOC(size * sizeof(AddressInformation)); + return addr; +} + +// Opaque datatype accessor addrinfo->ai_family +int GetAddressFamily(AddressInformation address) +{ + return address->addr.ai_family; +} + +// Opaque datatype accessor addrinfo->ai_socktype +int GetAddressSocketType(AddressInformation address) +{ + return address->addr.ai_socktype; +} + +// Opaque datatype accessor addrinfo->ai_protocol +int GetAddressProtocol(AddressInformation address) +{ + return address->addr.ai_protocol; +} + +// Opaque datatype accessor addrinfo->ai_canonname +char *GetAddressCanonName(AddressInformation address) +{ + return address->addr.ai_canonname; +} + +// Opaque datatype accessor addrinfo->ai_addr +char *GetAddressHostAndPort(AddressInformation address, char *outhost, unsigned short *outport) +{ + //char *ip[INET6_ADDRSTRLEN]; + char *result = NULL; + struct sockaddr_storage *storage = (struct sockaddr_storage *)address->addr.ai_addr; + + switch (storage->ss_family) + { + case AF_INET: + { + struct sockaddr_in *s = ((struct sockaddr_in *)address->addr.ai_addr); + //result = inet_ntop(AF_INET, &s->sin_addr, ip, INET_ADDRSTRLEN); // TODO. + *outport = ntohs(s->sin_port); + } break; + case AF_INET6: + { + struct sockaddr_in6 *s = ((struct sockaddr_in6 *)address->addr.ai_addr); + //result = inet_ntop(AF_INET6, &s->sin6_addr, ip, INET6_ADDRSTRLEN); // TODO. + *outport = ntohs(s->sin6_port); + } break; + default: break; + } + + if (result == NULL) + { + TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(SocketGetLastError())); + SocketSetLastError(0); + } + else + { + strcpy(outhost, result); + } + return result; +} + +// +void PacketSend(Packet *packet) +{ + TRACELOG(LOG_INFO, "Sending packet (%s) with size %d\n", packet->data, packet->size); +} + +// +void PacketReceive(Packet *packet) +{ + TRACELOG(LOG_INFO, "Receiving packet (%s) with size %d\n", packet->data, packet->size); +} + +// +void PacketWrite16(Packet *packet, uint16_t value) +{ + TRACELOG(LOG_INFO, "Original: 0x%04" PRIX16 " - %" PRIu16 "\n", value, value); + uint8_t *data = packet->data + packet->offs; + *data++ = (uint8_t)(value >> 8); + *data++ = (uint8_t)(value); + packet->size += sizeof(uint16_t); + packet->offs += sizeof(uint16_t); + TRACELOG(LOG_INFO, "Network: 0x%04" PRIX16 " - %" PRIu16 "\n", (uint16_t) *data, (uint16_t) *data); +} + +// +void PacketWrite32(Packet *packet, uint32_t value) +{ + TRACELOG(LOG_INFO, "Original: 0x%08" PRIX32 " - %" PRIu32 "\n", value, value); + uint8_t *data = packet->data + packet->offs; + *data++ = (uint8_t)(value >> 24); + *data++ = (uint8_t)(value >> 16); + *data++ = (uint8_t)(value >> 8); + *data++ = (uint8_t)(value); + packet->size += sizeof(uint32_t); + packet->offs += sizeof(uint32_t); + + TRACELOG(LOG_INFO, "Network: 0x%08" PRIX32 " - %" PRIu32 "\n", + (uint32_t)(((intptr_t) packet->data) - packet->offs), + (uint32_t)(((intptr_t) packet->data) - packet->offs)); +} + +// +void PacketWrite64(Packet *packet, uint64_t value) +{ + TRACELOG(LOG_INFO, "Original: 0x%016" PRIX64 " - %" PRIu64 "\n", value, value); + + uint8_t *data = packet->data + packet->offs; + *data++ = (uint8_t)(value >> 56); + *data++ = (uint8_t)(value >> 48); + *data++ = (uint8_t)(value >> 40); + *data++ = (uint8_t)(value >> 32); + *data++ = (uint8_t)(value >> 24); + *data++ = (uint8_t)(value >> 16); + *data++ = (uint8_t)(value >> 8); + *data++ = (uint8_t)(value); + packet->size += sizeof(uint64_t); + packet->offs += sizeof(uint64_t); + + TRACELOG(LOG_INFO, "Network: 0x%016" PRIX64 " - %" PRIu64 "\n", (uint64_t)(packet->data - packet->offs), (uint64_t)(packet->data - packet->offs)); +} + +// +uint16_t PacketRead16(Packet *packet) +{ + uint8_t *data = packet->data + packet->offs; + packet->size += sizeof(uint16_t); + packet->offs += sizeof(uint16_t); + uint16_t value = ((uint16_t) data[0] << 8) | data[1]; + TRACELOG(LOG_INFO, "Original: 0x%04" PRIX16 " - %" PRIu16 "\n", value, value); + + return value; +} + +// +uint32_t PacketRead32(Packet *packet) +{ + uint8_t *data = packet->data + packet->offs; + packet->size += sizeof(uint32_t); + packet->offs += sizeof(uint32_t); + uint32_t value = ((uint32_t) data[0] << 24) | ((uint32_t) data[1] << 16) | ((uint32_t) data[2] << 8) | data[3]; + TRACELOG(LOG_INFO, "Original: 0x%08" PRIX32 " - %" PRIu32 "\n", value, value); + + return value; +} + +// +uint64_t PacketRead64(Packet *packet) +{ + uint8_t *data = packet->data + packet->offs; + packet->size += sizeof(uint64_t); + packet->offs += sizeof(uint64_t); + uint64_t value = ((uint64_t) data[0] << 56) | ((uint64_t) data[1] << 48) | ((uint64_t) data[2] << 40) | ((uint64_t) data[3] << 32) | ((uint64_t) data[4] << 24) | ((uint64_t) data[5] << 16) | ((uint64_t) data[6] << 8) | data[7]; + TRACELOG(LOG_INFO, "Original: 0x%016" PRIX64 " - %" PRIu64 "\n", value, value); + + return value; +} + #endif // RNET_IMPLEMENTATION \ No newline at end of file From 2760304d668aa886b0170922a0aa194e541e68b5 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 31 May 2021 12:27:26 +0200 Subject: [PATCH 27/38] Update CMakeLists.txt --- src/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 38ca3f404..3dcd96bfb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -24,7 +24,6 @@ endif() set(raylib_public_headers raylib.h rlgl.h - physac.h raymath.h raudio.h ) From 565319184cbba9ba76fd855d5d23fa2691e9d75f Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 31 May 2021 12:37:03 +0200 Subject: [PATCH 28/38] Reviewed struct field name --- parser/README.md | 4 ++-- parser/raylib_parser.c | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/parser/README.md b/parser/README.md index 85c212666..a21d67878 100644 --- a/parser/README.md +++ b/parser/README.md @@ -32,7 +32,7 @@ This parser is specifically designed to work with raylib.h, so, it has some cons ``` typedef enum { - = , + = , , , @@ -41,7 +41,7 @@ This parser is specifically designed to work with raylib.h, so, it has some cons _NOTE: For enums, multiple options are supported:_ - - If value is not provided, ( + 1) is assigned + - If value is not provided, ( + 1) is assigned - Value description can be provided or not ## Additional notes diff --git a/parser/raylib_parser.c b/parser/raylib_parser.c index 4655fd14a..da426a97b 100644 --- a/parser/raylib_parser.c +++ b/parser/raylib_parser.c @@ -32,14 +32,14 @@ typedef enum { - = , + = , , , } ; NOTE: Multiple options are supported: - - If value is not provided, ( + 1) is assigned + - If value is not provided, ( + 1) is assigned - Value description can be provided or not This parser could work with other C header files if mentioned constraints are followed. @@ -95,7 +95,7 @@ typedef struct EnumInfo { char desc[64]; // Enum description int valueCount; // Number of values in enumerator char valueName[128][64]; // Value name definition (max: 128 values) - int valueInt[128]; // Value integer (max: 128 values) + int valueInteger[128]; // Value integer (max: 128 values) char valueDesc[128][64]; // Value description (max: 128 values) } EnumInfo; @@ -362,14 +362,14 @@ int main() c++; n++; } - if (integer[1] == 'x') enums[i].valueInt[enums[i].valueCount] = (int)strtol(integer, NULL, 16); - else enums[i].valueInt[enums[i].valueCount] = atoi(integer); + if (integer[1] == 'x') enums[i].valueInteger[enums[i].valueCount] = (int)strtol(integer, NULL, 16); + else enums[i].valueInteger[enums[i].valueCount] = atoi(integer); } - else enums[i].valueInt[enums[i].valueCount] = (enums[i].valueInt[enums[i].valueCount - 1] + 1); + else enums[i].valueInteger[enums[i].valueCount] = (enums[i].valueInteger[enums[i].valueCount - 1] + 1); // TODO: Parse value description if any } - else enums[i].valueInt[enums[i].valueCount] = (enums[i].valueInt[enums[i].valueCount - 1] + 1); + else enums[i].valueInteger[enums[i].valueCount] = (enums[i].valueInteger[enums[i].valueCount - 1] + 1); enums[i].valueCount++; } @@ -480,7 +480,7 @@ int main() { printf("Enum %02i: %s (%i values)\n", i + 1, enums[i].name, enums[i].valueCount); //printf("Description: %s\n", enums[i].desc); - for (int e = 0; e < enums[i].valueCount; e++) printf(" Value %s: %i\n", enums[i].valueName[e], enums[i].valueInt[e]); + for (int e = 0; e < enums[i].valueCount; e++) printf(" Value %s: %i\n", enums[i].valueName[e], enums[i].valueInteger[e]); } // Print function info From 8f4261e9d40b519295591087c737ce48eb83ca6e Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 31 May 2021 12:58:49 +0200 Subject: [PATCH 29/38] Remove unused declarations --- src/core.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/core.c b/src/core.c index 5d7b0eca3..9ad6c8905 100644 --- a/src/core.c +++ b/src/core.c @@ -270,12 +270,6 @@ #include // Emscripten HTML5 library #endif -#if defined(SUPPORT_COMPRESSION_API) - // NOTE: Those declarations require stb_image and stb_image_write definitions, included in textures module - unsigned char *stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality); - char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen); -#endif - //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- From b359a1575e9adaab886e906fe8c24e665b3852ca Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 31 May 2021 18:27:53 +0200 Subject: [PATCH 30/38] Update core_3d_picking.c --- examples/core/core_3d_picking.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/core/core_3d_picking.c b/examples/core/core_3d_picking.c index 58d752819..1c42de79f 100644 --- a/examples/core/core_3d_picking.c +++ b/examples/core/core_3d_picking.c @@ -33,7 +33,7 @@ int main(void) Ray ray = { 0 }; // Picking line ray - bool collision = false; + RayCollision collision = { 0 }; SetCameraMode(camera, CAMERA_FREE); // Set a free camera mode @@ -49,16 +49,16 @@ int main(void) if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { - if (!collision) + if (!collision.hit) { ray = GetMouseRay(GetMousePosition(), camera); // Check collision between ray and box - collision = CheckCollisionRayBox(ray, + collision = GetRayCollisionBox(ray, (BoundingBox){(Vector3){ cubePosition.x - cubeSize.x/2, cubePosition.y - cubeSize.y/2, cubePosition.z - cubeSize.z/2 }, (Vector3){ cubePosition.x + cubeSize.x/2, cubePosition.y + cubeSize.y/2, cubePosition.z + cubeSize.z/2 }}); } - else collision = false; + else collision.hit = false; } //---------------------------------------------------------------------------------- @@ -70,7 +70,7 @@ int main(void) BeginMode3D(camera); - if (collision) + if (collision.hit) { DrawCube(cubePosition, cubeSize.x, cubeSize.y, cubeSize.z, RED); DrawCubeWires(cubePosition, cubeSize.x, cubeSize.y, cubeSize.z, MAROON); @@ -90,7 +90,7 @@ int main(void) DrawText("Try selecting the box with mouse!", 240, 10, 20, DARKGRAY); - if (collision) DrawText("BOX SELECTED", (screenWidth - MeasureText("BOX SELECTED", 30)) / 2, (int)(screenHeight * 0.1f), 30, GREEN); + if (collision.hit) DrawText("BOX SELECTED", (screenWidth - MeasureText("BOX SELECTED", 30)) / 2, (int)(screenHeight * 0.1f), 30, GREEN); DrawFPS(10, 10); From 9cc2cee9367363b6b35c3dae2d63b2838e2ae9d2 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 31 May 2021 18:32:35 +0200 Subject: [PATCH 31/38] Update text_draw_3d.c --- examples/text/text_draw_3d.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/text/text_draw_3d.c b/examples/text/text_draw_3d.c index bc4c55293..a579a528b 100644 --- a/examples/text/text_draw_3d.c +++ b/examples/text/text_draw_3d.c @@ -191,10 +191,10 @@ int main(void) Ray ray = GetMouseRay(GetMousePosition(), camera); // Check collision between ray and box - bool collision = CheckCollisionRayBox(ray, + RayCollision collision = GetRayCollisionBox(ray, (BoundingBox){(Vector3){ cubePosition.x - cubeSize.x/2, cubePosition.y - cubeSize.y/2, cubePosition.z - cubeSize.z/2 }, (Vector3){ cubePosition.x + cubeSize.x/2, cubePosition.y + cubeSize.y/2, cubePosition.z + cubeSize.z/2 }}); - if (collision) + if (collision.hit) { // Generate new random colors light = GenerateRandomColor(0.5f, 0.78f); From d39d26f27504945816d9e1caea2d959d1ee336d5 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 31 May 2021 18:36:03 +0200 Subject: [PATCH 32/38] Update models_loading.c --- examples/models/models_loading.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/models/models_loading.c b/examples/models/models_loading.c index 91e25f4dc..16f122ee8 100644 --- a/examples/models/models_loading.c +++ b/examples/models/models_loading.c @@ -98,7 +98,7 @@ int main(void) if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { // Check collision between ray and box - if (CheckCollisionRayBox(GetMouseRay(GetMousePosition(), camera), bounds)) selected = !selected; + if (GetRayCollisionBox(GetMouseRay(GetMousePosition(), camera), bounds).hit) selected = !selected; else selected = false; } //---------------------------------------------------------------------------------- From e991fefb897ba703c7bd6b2fbfa49319eecd0752 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 31 May 2021 18:51:56 +0200 Subject: [PATCH 33/38] Update Makefile --- examples/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/Makefile b/examples/Makefile index 475f524d6..dd1a84725 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -257,8 +257,8 @@ ifeq ($(PLATFORM),PLATFORM_WEB) endif # Define include paths for required headers -# NOTE: Several external required libraries (stb and others) -INCLUDE_PATHS = -I. -I$(RAYLIB_PATH)/src -I$(RAYLIB_PATH)/src/external +# NOTE: Some external/extras libraries could be required (stb, physac, easings...) +INCLUDE_PATHS = -I. -I$(RAYLIB_PATH)/src -I$(RAYLIB_PATH)/src/external -I$(RAYLIB_PATH)/src/extras # Define additional directories containing required header files ifeq ($(PLATFORM),PLATFORM_RPI) From 23398f01983d1d04298ecf3ea164199b30b1e14e Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 31 May 2021 19:02:26 +0200 Subject: [PATCH 34/38] Update Makefile --- examples/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/Makefile b/examples/Makefile index dd1a84725..432455f18 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -277,7 +277,7 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) INCLUDE_PATHS += -I/usr/local/include endif ifeq ($(PLATFORM_OS),LINUX) - INCLUDE_PATHS = -I$(RAYLIB_H_INSTALL_PATH) -I. -I$(RAYLIB_PATH)/src -I$(RAYLIB_PATH)/src/external + INCLUDE_PATHS += -I$(RAYLIB_H_INSTALL_PATH) endif endif From 5e3cf2864239ff9721595d7a67395418398cb1de Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 31 May 2021 19:32:48 +0200 Subject: [PATCH 35/38] Update raudio.c --- src/raudio.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/raudio.c b/src/raudio.c index fcc914be4..c57c66181 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -478,7 +478,7 @@ void InitAudioDevice(void) // Init dummy audio buffers pool for multichannel sound playing for (int i = 0; i < MAX_AUDIO_BUFFER_POOL_CHANNELS; i++) { - // WARNING: An empty audioBuffer is created (data = 0) + // WARNING: An empty audio buffer is created (data = 0) // AudioBuffer data just points to loaded sound data AUDIO.MultiChannel.pool[i] = LoadAudioBuffer(AUDIO_DEVICE_FORMAT, AUDIO_DEVICE_CHANNELS, AUDIO.System.device.sampleRate, 0, AUDIO_BUFFER_USAGE_STATIC); } @@ -2075,6 +2075,7 @@ static ma_uint32 ReadAudioBufferFramesInMixingFormat(AudioBuffer *audioBuffer, f // Sending audio data to device callback function +// This function will be called when miniaudio needs more data // NOTE: All the mixing takes place here static void OnSendAudioDataToDevice(ma_device *pDevice, void *pFramesOut, const void *pFramesInput, ma_uint32 frameCount) { From 0870295a7c0f4bbce8141174129e6085dc4bebbc Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 31 May 2021 20:40:27 +0200 Subject: [PATCH 36/38] RENAMED: enum Gestures -> Gesture Consistency tweak --- src/raylib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raylib.h b/src/raylib.h index 5f57496c1..9e0d36879 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -866,7 +866,7 @@ typedef enum { GESTURE_SWIPE_DOWN = 128, GESTURE_PINCH_IN = 256, GESTURE_PINCH_OUT = 512 -} Gestures; +} Gesture; // Camera system modes typedef enum { From 1a7dace57e8bd38f40fa87f30b46a855a4b71063 Mon Sep 17 00:00:00 2001 From: "Marco G. Maia" Date: Tue, 1 Jun 2021 10:00:19 -0300 Subject: [PATCH 37/38] Update docs for supported blend modes (#1801) --- src/core.c | 4 ++-- src/raylib.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core.c b/src/core.c index 9ad6c8905..87f8ca90d 100644 --- a/src/core.c +++ b/src/core.c @@ -2034,8 +2034,8 @@ void EndShaderMode(void) rlSetShader(rlGetShaderDefault()); } -// Begin blending mode (alpha, additive, multiplied) -// NOTE: Only 3 blending modes supported, default blend mode is alpha +// Begin blending mode (alpha, additive, multiplied, subtract, custom) +// NOTE: Blend modes supported are enumerated in BlendMode enum void BeginBlendMode(int mode) { rlSetBlendMode(mode); diff --git a/src/raylib.h b/src/raylib.h index 9e0d36879..db9bb4019 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -973,7 +973,7 @@ RLAPI void BeginTextureMode(RenderTexture2D target); // Begin drawi RLAPI void EndTextureMode(void); // Ends drawing to render texture RLAPI void BeginShaderMode(Shader shader); // Begin custom shader drawing RLAPI void EndShaderMode(void); // End custom shader drawing (use default shader) -RLAPI void BeginBlendMode(int mode); // Begin blending mode (alpha, additive, multiplied) +RLAPI void BeginBlendMode(int mode); // Begin blending mode (alpha, additive, multiplied, subtract, custom) RLAPI void EndBlendMode(void); // End blending mode (reset to default: alpha blending) RLAPI void BeginScissorMode(int x, int y, int width, int height); // Begin scissor mode (define screen area for following drawing) RLAPI void EndScissorMode(void); // End scissor mode From d1ac38171c7d6a64144e3a713e7da481f1f340ac Mon Sep 17 00:00:00 2001 From: isaac553876299 <60979696+isaac553876299@users.noreply.github.com> Date: Tue, 1 Jun 2021 17:00:40 +0200 Subject: [PATCH 38/38] Update raymath.h (#1802) --- src/raymath.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raymath.h b/src/raymath.h index 9d840443d..7d1e36266 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -326,7 +326,7 @@ RMDEF Vector2 Vector2MoveTowards(Vector2 v, Vector2 target, float maxDistance) float dy = target.y - v.y; float value = (dx*dx) + (dy*dy); - if ((value == 0) || ((maxDistance >= 0) && (value <= maxDistance*maxDistance))) result = target; + if ((value == 0) || ((maxDistance >= 0) && (value <= maxDistance * maxDistance))) return target; float dist = sqrtf(value);