|
@ -116,14 +116,11 @@ static const char *exCategories[REXM_MAX_EXAMPLE_CATEGORIES] = { "core", "shapes |
|
|
|
|
|
|
|
|
// Paths required for examples management |
|
|
// Paths required for examples management |
|
|
// TODO: Avoid hardcoding path values... |
|
|
// TODO: Avoid hardcoding path values... |
|
|
static const char *exBasePath = "C:/GitHub/raylib/examples"; |
|
|
|
|
|
static const char *exWebPath = "C:/GitHub/raylib.com/examples"; |
|
|
|
|
|
static const char *exTemplateFilePath = "C:/GitHub/raylib/examples/examples_template.c"; |
|
|
|
|
|
static const char *exTemplateScreenshot = "C:/GitHub/raylib/examples/examples_template.png"; |
|
|
|
|
|
static const char *exCollectionFilePath = "C:/GitHub/raylib/examples/examples_list.txt"; |
|
|
|
|
|
|
|
|
|
|
|
//const char *exBasePath = getenv("REXM_EXAMPLES_PATH"); |
|
|
|
|
|
//if (!exBasePath) exBasePath = "default/path"; |
|
|
|
|
|
|
|
|
static const char *exBasePath = NULL; //"C:/GitHub/raylib/examples"; |
|
|
|
|
|
static const char *exWebPath = NULL; //"C:/GitHub/raylib.com/examples"; |
|
|
|
|
|
static const char *exTemplateFilePath = NULL; //"C:/GitHub/raylib/examples/examples_template.c"; |
|
|
|
|
|
static const char *exTemplateScreenshot = NULL; //"C:/GitHub/raylib/examples/examples_template.png"; |
|
|
|
|
|
static const char *exCollectionFilePath = NULL; //"C:/GitHub/raylib/examples/examples_list.txt"; |
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------------- |
|
|
//---------------------------------------------------------------------------------- |
|
|
// Module specific functions declaration |
|
|
// Module specific functions declaration |
|
@ -150,8 +147,9 @@ static void UnloadExamplesData(rlExampleInfo *exInfo); |
|
|
static char **LoadTextLines(const char *text, int *count); |
|
|
static char **LoadTextLines(const char *text, int *count); |
|
|
static void UnloadTextLines(char **text); |
|
|
static void UnloadTextLines(char **text); |
|
|
|
|
|
|
|
|
// Get example info from file header |
|
|
|
|
|
static rlExampleInfo *GetExampleInfo(const char *exFileName); |
|
|
|
|
|
|
|
|
// Load example info from file header |
|
|
|
|
|
static rlExampleInfo *LoadExampleInfo(const char *exFileName); |
|
|
|
|
|
static void UnloadExampleInfo(rlExampleInfo *exInfo); |
|
|
|
|
|
|
|
|
// raylib example line info parser |
|
|
// raylib example line info parser |
|
|
// Parses following line format: core/core_basic_window;⭐️☆☆☆;1.0;1.0;"Ray"/@raysan5 |
|
|
// Parses following line format: core/core_basic_window;⭐️☆☆☆;1.0;1.0;"Ray"/@raysan5 |
|
@ -172,6 +170,19 @@ static void ClearExampleResources(char **resPaths); |
|
|
//------------------------------------------------------------------------------------ |
|
|
//------------------------------------------------------------------------------------ |
|
|
int main(int argc, char *argv[]) |
|
|
int main(int argc, char *argv[]) |
|
|
{ |
|
|
{ |
|
|
|
|
|
// Path values can be configured with environment variables |
|
|
|
|
|
exBasePath = getenv("REXM_EXAMPLES_BASE_PATH"); |
|
|
|
|
|
exWebPath = getenv("REXM_EXAMPLES_WEB_PATH"); |
|
|
|
|
|
exTemplateFilePath = getenv("REXM_EXAMPLES_TEMPLATE_FILE_PATH"); |
|
|
|
|
|
exTemplateScreenshot = getenv("REXM_EXAMPLES_TEMPLATE_SCREENSHOT_PATH"); |
|
|
|
|
|
exCollectionFilePath = getenv("REXM_EXAMPLES_COLLECTION_FILE_PATH"); |
|
|
|
|
|
|
|
|
|
|
|
if (!exBasePath) exBasePath = "C:/GitHub/raylib/examples"; |
|
|
|
|
|
if (!exWebPath) exWebPath = "C:/GitHub/raylib.com/examples"; |
|
|
|
|
|
if (!exTemplateFilePath) exTemplateFilePath = "C:/GitHub/raylib/examples/examples_template.c"; |
|
|
|
|
|
if (!exTemplateScreenshot) exTemplateScreenshot = "C:/GitHub/raylib/examples/examples_template.png"; |
|
|
|
|
|
if (!exCollectionFilePath) exCollectionFilePath = "C:/GitHub/raylib/examples/examples_list.txt"; |
|
|
|
|
|
|
|
|
char inFileName[1024] = { 0 }; // Example input filename (to be added) |
|
|
char inFileName[1024] = { 0 }; // Example input filename (to be added) |
|
|
|
|
|
|
|
|
char exName[64] = { 0 }; // Example name, without extension: core_basic_window |
|
|
char exName[64] = { 0 }; // Example name, without extension: core_basic_window |
|
@ -431,7 +442,7 @@ int main(int argc, char *argv[]) |
|
|
|
|
|
|
|
|
// Get required example info from example file header (if provided) |
|
|
// Get required example info from example file header (if provided) |
|
|
// NOTE: If no example info is provided (other than category/name), just using some default values |
|
|
// NOTE: If no example info is provided (other than category/name), just using some default values |
|
|
rlExampleInfo *exInfo = GetExampleInfo(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName)); |
|
|
|
|
|
|
|
|
rlExampleInfo *exInfo = LoadExampleInfo(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName)); |
|
|
|
|
|
|
|
|
// Get example difficulty stars |
|
|
// Get example difficulty stars |
|
|
char starsText[16] = { 0 }; |
|
|
char starsText[16] = { 0 }; |
|
@ -460,7 +471,7 @@ int main(int argc, char *argv[]) |
|
|
memcpy(exCollectionListUpdated + categoryIndex + textWritenSize, exCollectionList + categoryIndex, strlen(exCollectionList) - categoryIndex); |
|
|
memcpy(exCollectionListUpdated + categoryIndex + textWritenSize, exCollectionList + categoryIndex, strlen(exCollectionList) - categoryIndex); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
RL_FREE(exInfo); |
|
|
|
|
|
|
|
|
UnloadExampleInfo(exInfo); |
|
|
|
|
|
|
|
|
SaveFileText(exCollectionFilePath, exCollectionListUpdated); |
|
|
SaveFileText(exCollectionFilePath, exCollectionListUpdated); |
|
|
RL_FREE(exCollectionListUpdated); |
|
|
RL_FREE(exCollectionListUpdated); |
|
@ -707,11 +718,11 @@ int main(int argc, char *argv[]) |
|
|
|
|
|
|
|
|
// Check all examples in collection [examples_list.txt] -> Source of truth! |
|
|
// Check all examples in collection [examples_list.txt] -> Source of truth! |
|
|
int exCollectionCount = 0; |
|
|
int exCollectionCount = 0; |
|
|
rlExampleInfo *exCollection = LoadExamplesData(exCollectionFilePath, "ALL", true, &exCollectionCount); |
|
|
|
|
|
|
|
|
rlExampleInfo *exCollection = LoadExamplesData(exCollectionFilePath, "ALL", false, &exCollectionCount); |
|
|
|
|
|
|
|
|
// TODO: Validate: Duplicate entries in collection list? |
|
|
// TODO: Validate: Duplicate entries in collection list? |
|
|
|
|
|
|
|
|
// Get status information for all examples, using "status" field in the struct |
|
|
|
|
|
|
|
|
// Set status information for all examples, using "status" field in the struct |
|
|
for (int i = 0; i < exCollectionCount; i++) |
|
|
for (int i = 0; i < exCollectionCount; i++) |
|
|
{ |
|
|
{ |
|
|
rlExampleInfo *exInfo = &exCollection[i]; |
|
|
rlExampleInfo *exInfo = &exCollection[i]; |
|
@ -726,7 +737,8 @@ int main(int argc, char *argv[]) |
|
|
// Validate: example screenshot is not the template default one |
|
|
// Validate: example screenshot is not the template default one |
|
|
Image imScreenshot = LoadImage(TextFormat("%s/%s/%s.png", exBasePath, exInfo->category, exInfo->name)); |
|
|
Image imScreenshot = LoadImage(TextFormat("%s/%s/%s.png", exBasePath, exInfo->category, exInfo->name)); |
|
|
Image imTemplate = LoadImage(TextFormat("%s/examples_template.png", exBasePath)); |
|
|
Image imTemplate = LoadImage(TextFormat("%s/examples_template.png", exBasePath)); |
|
|
if (memcmp(imScreenshot.data, imTemplate.data, GetPixelDataSize(imScreenshot.width, imScreenshot.height, imScreenshot.format)) != 0) exInfo->status |= VALID_INVALID_PNG; |
|
|
|
|
|
|
|
|
if (memcmp(imScreenshot.data, imTemplate.data, GetPixelDataSize(imScreenshot.width, imScreenshot.height, imScreenshot.format)) == 0) |
|
|
|
|
|
exInfo->status |= VALID_INVALID_PNG; |
|
|
UnloadImage(imTemplate); |
|
|
UnloadImage(imTemplate); |
|
|
UnloadImage(imScreenshot); |
|
|
UnloadImage(imScreenshot); |
|
|
|
|
|
|
|
@ -737,13 +749,13 @@ int main(int argc, char *argv[]) |
|
|
if (FileTextFind(TextFormat("%s/Makefile.Web", exBasePath), exInfo->name) == -1) exInfo->status |= VALID_NOT_IN_MAKEFILE_WEB; |
|
|
if (FileTextFind(TextFormat("%s/Makefile.Web", exBasePath), exInfo->name) == -1) exInfo->status |= VALID_NOT_IN_MAKEFILE_WEB; |
|
|
|
|
|
|
|
|
// Validate: raylib/examples/README.md -> Example listed? |
|
|
// Validate: raylib/examples/README.md -> Example listed? |
|
|
if (FileTextFind(TextFormat("%s/README.md", exBasePath), exInfo->name) == -1) exInfo->status |= VALID_NOT_IN_JS; |
|
|
|
|
|
|
|
|
if (FileTextFind(TextFormat("%s/README.md", exBasePath), exInfo->name) == -1) exInfo->status |= VALID_NOT_IN_README; |
|
|
|
|
|
|
|
|
// Validate: raylib.com/common/examples.js -> Example listed? |
|
|
// Validate: raylib.com/common/examples.js -> Example listed? |
|
|
if (FileTextFind(TextFormat("%s/common/examples.js", exWebPath), exInfo->name) == -1) exInfo->status |= VALID_NOT_IN_README; |
|
|
|
|
|
|
|
|
if (FileTextFind(TextFormat("%s/../common/examples.js", exWebPath), exInfo->name + TextFindIndex(exInfo->name, "_") + 1) == -1) exInfo->status |= VALID_NOT_IN_JS; |
|
|
|
|
|
|
|
|
// Validate: raylib/projects/VS2022/examples/<category>_example_name.vcxproj -> File exists? |
|
|
// Validate: raylib/projects/VS2022/examples/<category>_example_name.vcxproj -> File exists? |
|
|
if (!FileExists(TextFormat("%s/../projects/VS2022/examples/%s.png", exBasePath, exInfo->name))) exInfo->status |= VALID_MISSING_VCXPROJ; |
|
|
|
|
|
|
|
|
if (!FileExists(TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exInfo->name))) exInfo->status |= VALID_MISSING_VCXPROJ; |
|
|
|
|
|
|
|
|
// Validate: raylib/projects/VS2022/raylib.sln -> Example listed? |
|
|
// Validate: raylib/projects/VS2022/raylib.sln -> Example listed? |
|
|
if (FileTextFind(TextFormat("%s/../projects/VS2022/raylib.sln", exBasePath), exInfo->name) == -1) exInfo->status |= VALID_NOT_IN_VCXSOL; |
|
|
if (FileTextFind(TextFormat("%s/../projects/VS2022/raylib.sln", exBasePath), exInfo->name) == -1) exInfo->status |= VALID_NOT_IN_VCXSOL; |
|
@ -764,13 +776,22 @@ int main(int argc, char *argv[]) |
|
|
for (int v = 0; v < 3; v++) |
|
|
for (int v = 0; v < 3; v++) |
|
|
{ |
|
|
{ |
|
|
char *resPathUpdated = TextReplace(resPaths[r], "glsl%i", TextFormat("glsl%i", glslVer[v])); |
|
|
char *resPathUpdated = TextReplace(resPaths[r], "glsl%i", TextFormat("glsl%i", glslVer[v])); |
|
|
if (!FileExists(TextFormat("%s/%s/%s", exBasePath, exInfo->category, resPathUpdated))) exInfo->status |= VALID_MISSING_RESOURCES; |
|
|
|
|
|
|
|
|
if (!FileExists(TextFormat("%s/%s/%s", exBasePath, exInfo->category, resPathUpdated))) |
|
|
|
|
|
{ |
|
|
|
|
|
exInfo->status |= VALID_MISSING_RESOURCES; |
|
|
|
|
|
// Logging missing resources for convenience |
|
|
|
|
|
LOG("WARNING: [%s] Missing resource: %s\n", exInfo->name, resPathUpdated); |
|
|
|
|
|
} |
|
|
RL_FREE(resPathUpdated); |
|
|
RL_FREE(resPathUpdated); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
else |
|
|
else |
|
|
{ |
|
|
{ |
|
|
if (!FileExists(TextFormat("%s/%s/%s", exBasePath, exInfo->category, resPaths[r]))) exInfo->status |= VALID_MISSING_RESOURCES; |
|
|
|
|
|
|
|
|
if (!FileExists(TextFormat("%s/%s/%s", exBasePath, exInfo->category, resPaths[r]))) |
|
|
|
|
|
{ |
|
|
|
|
|
exInfo->status |= VALID_MISSING_RESOURCES; |
|
|
|
|
|
LOG("WARNING: [%s] Missing resource: %s\n", exInfo->name, resPaths[r]); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
@ -780,21 +801,22 @@ int main(int argc, char *argv[]) |
|
|
// Validate: raylib.com/examples/<category>/<category>_example_name.data -> File exists? |
|
|
// Validate: raylib.com/examples/<category>/<category>_example_name.data -> File exists? |
|
|
// Validate: raylib.com/examples/<category>/<category>_example_name.wasm -> File exists? |
|
|
// Validate: raylib.com/examples/<category>/<category>_example_name.wasm -> File exists? |
|
|
// Validate: raylib.com/examples/<category>/<category>_example_name.js -> File exists? |
|
|
// Validate: raylib.com/examples/<category>/<category>_example_name.js -> File exists? |
|
|
if ((!FileExists(TextFormat("%s/examples/%s/%s.html", exWebPath, exInfo->category, exInfo->name))) || |
|
|
|
|
|
((exInfo->resCount > 0) && !FileExists(TextFormat("%s/examples/%s/%s.data", exWebPath, exInfo->category, exInfo->name))) || |
|
|
|
|
|
(!FileExists(TextFormat("%s/examples/%s/%s.wasm", exWebPath, exInfo->category, exInfo->name))) || |
|
|
|
|
|
(!FileExists(TextFormat("%s/examples/%s/%s.js", exWebPath, exInfo->category, exInfo->name)))) exInfo->status |= VALID_MISSING_WEB_OUTPUT; |
|
|
|
|
|
|
|
|
if (!FileExists(TextFormat("%s/%s/%s.html", exWebPath, exInfo->category, exInfo->name)) || |
|
|
|
|
|
!FileExists(TextFormat("%s/%s/%s.wasm", exWebPath, exInfo->category, exInfo->name)) || |
|
|
|
|
|
!FileExists(TextFormat("%s/%s/%s.js", exWebPath, exInfo->category, exInfo->name)) || |
|
|
|
|
|
((exInfo->resCount > 0) && !FileExists(TextFormat("%s/%s/%s.data", exWebPath, exInfo->category, exInfo->name)))) |
|
|
|
|
|
exInfo->status |= VALID_MISSING_WEB_OUTPUT; |
|
|
|
|
|
|
|
|
// NOTE: Additional validation elements |
|
|
// NOTE: Additional validation elements |
|
|
// Validate: Example naming conventions: <category>/<category>_example_name, valid category |
|
|
// Validate: Example naming conventions: <category>/<category>_example_name, valid category |
|
|
if ((TextFindIndex(exInfo->name, exInfo->category) == -1) || |
|
|
if ((TextFindIndex(exInfo->name, exInfo->category) == -1) || |
|
|
(!TextIsEqual(exInfo->category, "core") || !TextIsEqual(exInfo->category, "shapes") || |
|
|
|
|
|
!TextIsEqual(exInfo->category, "textures") || !TextIsEqual(exInfo->category, "text") || |
|
|
|
|
|
!TextIsEqual(exInfo->category, "models") || !TextIsEqual(exInfo->category, "shaders") || |
|
|
|
|
|
!TextIsEqual(exInfo->category, "audio") || !TextIsEqual(exInfo->category, "others"))) exInfo->status |= VALID_INVALID_CATEGORY; |
|
|
|
|
|
|
|
|
(!TextIsEqual(exInfo->category, "core") && !TextIsEqual(exInfo->category, "shapes") && |
|
|
|
|
|
!TextIsEqual(exInfo->category, "textures") && !TextIsEqual(exInfo->category, "text") && |
|
|
|
|
|
!TextIsEqual(exInfo->category, "models") && !TextIsEqual(exInfo->category, "shaders") && |
|
|
|
|
|
!TextIsEqual(exInfo->category, "audio") && !TextIsEqual(exInfo->category, "others"))) exInfo->status |= VALID_INVALID_CATEGORY; |
|
|
|
|
|
|
|
|
// Validate: Example info (stars, author, github) missmatches with example header content |
|
|
// Validate: Example info (stars, author, github) missmatches with example header content |
|
|
rlExampleInfo *exInfoHeader = GetExampleInfo(TextFormat("%s/%s/%s.c", exBasePath, exInfo->category, exInfo->name)); |
|
|
|
|
|
|
|
|
rlExampleInfo *exInfoHeader = LoadExampleInfo(TextFormat("%s/%s/%s.c", exBasePath, exInfo->category, exInfo->name)); |
|
|
|
|
|
|
|
|
if ((strcmp(exInfo->name, exInfoHeader->name) != 0) || // NOTE: Get it from example, not file |
|
|
if ((strcmp(exInfo->name, exInfoHeader->name) != 0) || // NOTE: Get it from example, not file |
|
|
(strcmp(exInfo->category, exInfoHeader->category) != 0) || |
|
|
(strcmp(exInfo->category, exInfoHeader->category) != 0) || |
|
@ -804,62 +826,119 @@ int main(int argc, char *argv[]) |
|
|
(exInfo->verCreated != exInfoHeader->verCreated) || |
|
|
(exInfo->verCreated != exInfoHeader->verCreated) || |
|
|
(exInfo->verUpdated != exInfoHeader->verUpdated)) exInfo->status |= VALID_INCONSISTENT_INFO; |
|
|
(exInfo->verUpdated != exInfoHeader->verUpdated)) exInfo->status |= VALID_INCONSISTENT_INFO; |
|
|
|
|
|
|
|
|
RL_FREE(exInfoHeader); |
|
|
|
|
|
|
|
|
UnloadExampleInfo(exInfoHeader); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// TODO: Generate validation report/table with results (.md) |
|
|
|
|
|
/* |
|
|
|
|
|
//Status Description |
|
|
|
|
|
//OK Everything found |
|
|
|
|
|
//MISSING One or more files are missing, some can be solved automatically |
|
|
|
|
|
//ERROR Serious inconsistencies |
|
|
|
|
|
|
|
|
|
|
|
Columns: |
|
|
|
|
|
[C] VALID_MISSING_C // Missing .c source file |
|
|
|
|
|
[PNG] VALID_MISSING_PNG // Missing screenshot .png |
|
|
|
|
|
[WPNG] VALID_INVALID_PNG // Invalid png screenshot (using template one) |
|
|
|
|
|
[RES] VALID_MISSING_RESOURCES // Missing resources listed in the code |
|
|
|
|
|
[VCX] VALID_MISSING_VCXPROJ // Missing Visual Studio .vcxproj file |
|
|
|
|
|
[SOL] VALID_NOT_IN_VCXSOL // Project not included in solution file |
|
|
|
|
|
[MK] VALID_NOT_IN_MAKEFILE // Not listed in Makefile |
|
|
|
|
|
[MKWEB] VALID_NOT_IN_MAKEFILE_WEB // Not listed in Makefile.Web |
|
|
|
|
|
[RDME] VALID_NOT_IN_README // Not listed in README.md |
|
|
|
|
|
[JS] VALID_NOT_IN_JS // Not listed in examples.js |
|
|
|
|
|
[WOUT] VALID_MISSING_WEB_OUTPUT // Missing .html/.data/.wasm/.js |
|
|
|
|
|
[INFO] VALID_INCONSISTENT_INFO // Inconsistent info between collection and example header (stars, author...) |
|
|
|
|
|
[CAT] VALID_INVALID_CATEGORY // Not a recognized category |
|
|
|
|
|
|
|
|
|
|
|
[STATUS] [CATEGORY] [NAME] [C] [PNG] [WPNG] [RES] [VCX] [SOL] [MK] [MKWEB] [RDME] [JS] [WOUT] [INFO] [CAT] |
|
|
|
|
|
----------------------------------------------------------------------------------------------------------------------- |
|
|
|
|
|
OK core basic_window ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ |
|
|
|
|
|
MISSING shapes colorful_lines ✘ ✔ ✘ ✔ ✘ ✔ ✔ ✘ ✔ ✔ ✔ ✔ ✔ |
|
|
|
|
|
ERROR text broken_shader ✘ ✘ ✘ ✘ ✘ ✘ ✘ ✘ ✔ ✘ ✔ ✔ ✔ |
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
// Generate validation report/table with results (.md) |
|
|
|
|
|
/* |
|
|
|
|
|
Columns: |
|
|
|
|
|
[C] VALID_MISSING_C // Missing .c source file |
|
|
|
|
|
[PNG] VALID_MISSING_PNG // Missing screenshot .png |
|
|
|
|
|
[WPNG] VALID_INVALID_PNG // Invalid png screenshot (using template one) |
|
|
|
|
|
[RES] VALID_MISSING_RESOURCES // Missing resources listed in the code |
|
|
|
|
|
[VCX] VALID_MISSING_VCXPROJ // Missing Visual Studio .vcxproj file |
|
|
|
|
|
[SOL] VALID_NOT_IN_VCXSOL // Project not included in solution file |
|
|
|
|
|
[MK] VALID_NOT_IN_MAKEFILE // Not listed in Makefile |
|
|
|
|
|
[MKWEB] VALID_NOT_IN_MAKEFILE_WEB // Not listed in Makefile.Web |
|
|
|
|
|
[RDME] VALID_NOT_IN_README // Not listed in README.md |
|
|
|
|
|
[JS] VALID_NOT_IN_JS // Not listed in examples.js |
|
|
|
|
|
[WOUT] VALID_MISSING_WEB_OUTPUT // Missing .html/.data/.wasm/.js |
|
|
|
|
|
[INFO] VALID_INCONSISTENT_INFO // Inconsistent info between collection and example header (stars, author...) |
|
|
|
|
|
[CAT] VALID_INVALID_CATEGORY // Not a recognized category |
|
|
|
|
|
|
|
|
|
|
|
| [EXAMPLE NAME] | [C] |[CAT]|[INFO]|[PNG]|[WPNG]|[RES]|[MK] |[MKWEB]|[VCX]|[SOL]|[RDME]|[JS] |[WOUT]| |
|
|
|
|
|
|:-----------------------------|:---:|:---:|:----:|:---:|:----:|:---:|:---:|:-----:|:---:|:---:|:----:|:---:|:----:| |
|
|
|
|
|
| core_basic_window | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | |
|
|
|
|
|
| shapes_colors_palette | ✘ | ✔ | ✘ | ✔ | ✘ | ✔ | ✔ | ✘ | ✔ | ✔ | ✔ | ✔ | ✔ | |
|
|
|
|
|
| text_format_text | ✘ | ✘ | ✘ | ✘ | ✘ | ✘ | ✘ | ✘ | ✔ | ✘ | ✔ | ✔ | ✔ | |
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
char *report = (char *)RL_CALLOC(REXM_MAX_BUFFER_SIZE, 1); |
|
|
|
|
|
|
|
|
|
|
|
int repIndex = 0; |
|
|
|
|
|
repIndex += sprintf(report + repIndex, "# EXAMPLES COLLECTION REPORT\n\n"); |
|
|
|
|
|
|
|
|
|
|
|
repIndex += sprintf(report + repIndex, "```\nExample elements validated:\n\n"); |
|
|
|
|
|
repIndex += sprintf(report + repIndex, " - [C] : Missing .c source file\n"); |
|
|
|
|
|
repIndex += sprintf(report + repIndex, " - [CAT] : Not a recognized category\n"); |
|
|
|
|
|
repIndex += sprintf(report + repIndex, " - [INFO] : Inconsistent example header info (stars, author...)\n"); |
|
|
|
|
|
repIndex += sprintf(report + repIndex, " - [PNG] : Missing screenshot .png\n"); |
|
|
|
|
|
repIndex += sprintf(report + repIndex, " - [WPNG] : Invalid png screenshot (using default one)\n"); |
|
|
|
|
|
repIndex += sprintf(report + repIndex, " - [RES] : Missing resources listed in the code\n"); |
|
|
|
|
|
repIndex += sprintf(report + repIndex, " - [MK] : Not listed in Makefile\n"); |
|
|
|
|
|
repIndex += sprintf(report + repIndex, " - [MKWEB] : Not listed in Makefile.Web\n"); |
|
|
|
|
|
repIndex += sprintf(report + repIndex, " - [VCX] : Missing Visual Studio project file\n"); |
|
|
|
|
|
repIndex += sprintf(report + repIndex, " - [SOL] : Project not included in solution file\n"); |
|
|
|
|
|
repIndex += sprintf(report + repIndex, " - [RDME] : Not listed in README.md\n"); |
|
|
|
|
|
repIndex += sprintf(report + repIndex, " - [JS] : Not listed in Web (examples.js)\n"); |
|
|
|
|
|
repIndex += sprintf(report + repIndex, " - [WOUT] : Missing Web build (.html/.data/.wasm/.js)\n```\n"); |
|
|
|
|
|
|
|
|
|
|
|
repIndex += sprintf(report + repIndex, "| **EXAMPLE NAME** | [C] | [CAT]| [INFO]|[PNG]|[WPNG]| [RES]| [MK] |[MKWEB]| [VCX]| [SOL]|[RDME]|[JS] | [WOUT]|\n"); |
|
|
|
|
|
repIndex += sprintf(report + repIndex, "|:---------------------------------|:---:|:----:|:-----:|:---:|:----:|:----:|:----:|:-----:|:----:|:----:|:----:|:---:|:-----:|\n"); |
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < exCollectionCount; i++) |
|
|
|
|
|
{ |
|
|
|
|
|
repIndex += sprintf(report + repIndex, "| %-32s | %s | %s | %s | %s | %s | %s | %s | %s | %s | %s | %s | %s | %s |\n", |
|
|
|
|
|
exCollection[i].name, |
|
|
|
|
|
(exCollection[i].status & VALID_MISSING_C)? "❌" : "✔", |
|
|
|
|
|
(exCollection[i].status & VALID_INVALID_CATEGORY)? "❌" : "✔", |
|
|
|
|
|
(exCollection[i].status & VALID_INCONSISTENT_INFO)? "❌" : "✔", |
|
|
|
|
|
(exCollection[i].status & VALID_MISSING_PNG)? "❌" : "✔", |
|
|
|
|
|
(exCollection[i].status & VALID_INVALID_PNG)? "❌" : "✔", |
|
|
|
|
|
(exCollection[i].status & VALID_MISSING_RESOURCES)? "❌" : "✔", |
|
|
|
|
|
(exCollection[i].status & VALID_NOT_IN_MAKEFILE)? "❌" : "✔", |
|
|
|
|
|
(exCollection[i].status & VALID_NOT_IN_MAKEFILE_WEB)? "❌" : "✔", |
|
|
|
|
|
(exCollection[i].status & VALID_MISSING_VCXPROJ)? "❌" : "✔", |
|
|
|
|
|
(exCollection[i].status & VALID_NOT_IN_VCXSOL)? "❌" : "✔", |
|
|
|
|
|
(exCollection[i].status & VALID_NOT_IN_README)? "❌" : "✔", |
|
|
|
|
|
(exCollection[i].status & VALID_NOT_IN_JS)? "❌" : "✔", |
|
|
|
|
|
(exCollection[i].status & VALID_MISSING_WEB_OUTPUT)? "❌" : "✔"); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
UnloadExamplesData(exCollection); |
|
|
|
|
|
//------------------------------------------------------------------------------------------------ |
|
|
|
|
|
|
|
|
SaveFileText(TextFormat("%s/../tools/rexm/%s", exBasePath, "examples_report.md"), report); |
|
|
|
|
|
RL_FREE(report); |
|
|
|
|
|
|
|
|
|
|
|
//UnloadExamplesData(exCollection); // Done at the end, it can be required for fixing |
|
|
|
|
|
|
|
|
if (opCode == OP_UPDATE) |
|
|
if (opCode == OP_UPDATE) |
|
|
{ |
|
|
{ |
|
|
// Actions to be performed to fix/review anything possible |
|
|
|
|
|
|
|
|
// Actions to fix/review anything possible from validation results |
|
|
//------------------------------------------------------------------------------------------------ |
|
|
//------------------------------------------------------------------------------------------------ |
|
|
|
|
|
|
|
|
// TODO: Process validation results and take actions to |
|
|
|
|
|
// correct everything possible to make collection consistent |
|
|
|
|
|
|
|
|
// Check examples "status" information |
|
|
|
|
|
for (int i = 0; i < exCollectionCount; i++) |
|
|
|
|
|
{ |
|
|
|
|
|
rlExampleInfo *exInfo = &exCollection[i]; |
|
|
|
|
|
|
|
|
// Review: Add/Remove: raylib/projects/VS2022/examples/<category>_example_name.vcxproj |
|
|
|
|
|
// Review: Add/remove: raylib/projects/VS2022/raylib.sln |
|
|
|
|
|
|
|
|
if (exInfo->status & VALID_MISSING_C) LOG("WARNING: [%s] Missing code file\n", exInfo->name); |
|
|
|
|
|
else |
|
|
|
|
|
{ |
|
|
|
|
|
// NOTE: Some issues can not be automatically fixed, only logged |
|
|
|
|
|
//if (exInfo->status & VALID_MISSING_PNG) LOG("WARNING: [%s] Missing screenshot file\n", exInfo->name); |
|
|
|
|
|
//if (exInfo->status & VALID_INVALID_PNG) LOG("WARNING: [%s] Invalid screenshot file (using template)\n", exInfo->name); |
|
|
|
|
|
//if (exInfo->status & VALID_MISSING_RESOURCES) LOG("WARNING: [%s] Missing resources detected\n", exInfo->name); |
|
|
|
|
|
//if (exInfo->status & VALID_INCONSISTENT_INFO) LOG("WARNING: [%s] Inconsistent example header info\n", exInfo->name); |
|
|
|
|
|
//if (exInfo->status & VALID_INVALID_CATEGORY) LOG("WARNING: [%s] Invalid example category\n", exInfo->name); |
|
|
|
|
|
|
|
|
|
|
|
// Review: Add/Remove: raylib/projects/VS2022/examples/<category>_example_name.vcxproj |
|
|
|
|
|
// Review: Add/remove: raylib/projects/VS2022/raylib.sln |
|
|
|
|
|
// Solves: VALID_MISSING_VCXPROJ, VALID_NOT_IN_VCXSOL |
|
|
|
|
|
|
|
|
|
|
|
// Review: Add/Remove: raylib.com/examples/<category>/<category>_example_name.html |
|
|
|
|
|
// Review: Add/Remove: raylib.com/examples/<category>/<category>_example_name.data |
|
|
|
|
|
// Review: Add/Remove: raylib.com/examples/<category>/<category>_example_name.wasm |
|
|
|
|
|
// Review: Add/Remove: raylib.com/examples/<category>/<category>_example_name.js |
|
|
|
|
|
// Solves: VALID_MISSING_WEB_OUTPUT |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// Update files: Makefile, Makefile.Web, README.md, examples.js |
|
|
// Update files: Makefile, Makefile.Web, README.md, examples.js |
|
|
UpdateRequiredFiles(); |
|
|
|
|
|
|
|
|
|
|
|
// Review examples |
|
|
|
|
|
// Review: Add/Remove: raylib.com/examples/<category>/<category>_example_name.html |
|
|
|
|
|
// Review: Add/Remove: raylib.com/examples/<category>/<category>_example_name.data |
|
|
|
|
|
// Review: Add/Remove: raylib.com/examples/<category>/<category>_example_name.wasm |
|
|
|
|
|
// Review: Add/Remove: raylib.com/examples/<category>/<category>_example_name.js |
|
|
|
|
|
|
|
|
// Solves: VALID_NOT_IN_MAKEFILE, VALID_NOT_IN_MAKEFILE_WEB, VALID_NOT_IN_README, VALID_NOT_IN_JS |
|
|
|
|
|
UpdateRequiredFiles(); |
|
|
//------------------------------------------------------------------------------------------------ |
|
|
//------------------------------------------------------------------------------------------------ |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
UnloadExamplesData(exCollection); |
|
|
|
|
|
//------------------------------------------------------------------------------------------------ |
|
|
|
|
|
|
|
|
} break; |
|
|
} break; |
|
|
default: // Help |
|
|
default: // Help |
|
@ -1414,7 +1493,7 @@ static void UnloadTextLines(char **lines) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Get example info from file header |
|
|
// Get example info from file header |
|
|
rlExampleInfo *GetExampleInfo(const char *exFileName) |
|
|
|
|
|
|
|
|
rlExampleInfo *LoadExampleInfo(const char *exFileName) |
|
|
{ |
|
|
{ |
|
|
rlExampleInfo *exInfo = (rlExampleInfo *)RL_CALLOC(1, sizeof(rlExampleInfo)); |
|
|
rlExampleInfo *exInfo = (rlExampleInfo *)RL_CALLOC(1, sizeof(rlExampleInfo)); |
|
|
|
|
|
|
|
@ -1427,6 +1506,7 @@ rlExampleInfo *GetExampleInfo(const char *exFileName) |
|
|
|
|
|
|
|
|
// Get example difficulty stars |
|
|
// Get example difficulty stars |
|
|
// NOTE: Counting the unicode char occurrences: ⭐️ |
|
|
// NOTE: Counting the unicode char occurrences: ⭐️ |
|
|
|
|
|
// WARNING: The stars unicode in examples is not the same than in collection list!!! |
|
|
int starsIndex = TextFindIndex(exText, "★"); |
|
|
int starsIndex = TextFindIndex(exText, "★"); |
|
|
if (starsIndex > 0) |
|
|
if (starsIndex > 0) |
|
|
{ |
|
|
{ |
|
@ -1434,8 +1514,8 @@ rlExampleInfo *GetExampleInfo(const char *exFileName) |
|
|
while (*starPtr) |
|
|
while (*starPtr) |
|
|
{ |
|
|
{ |
|
|
if (((unsigned char)starPtr[0] == 0xe2) && |
|
|
if (((unsigned char)starPtr[0] == 0xe2) && |
|
|
((unsigned char)starPtr[1] == 0xad) && |
|
|
|
|
|
((unsigned char)starPtr[2] == 0x90)) |
|
|
|
|
|
|
|
|
((unsigned char)starPtr[1] == 0x98) && |
|
|
|
|
|
((unsigned char)starPtr[2] == 0x85)) |
|
|
{ |
|
|
{ |
|
|
exInfo->stars++; |
|
|
exInfo->stars++; |
|
|
starPtr += 3; // Advance past multibyte character |
|
|
starPtr += 3; // Advance past multibyte character |
|
@ -1459,31 +1539,32 @@ rlExampleInfo *GetExampleInfo(const char *exFileName) |
|
|
exInfo->verUpdated = TextToFloat(verUpdateText); |
|
|
exInfo->verUpdated = TextToFloat(verUpdateText); |
|
|
|
|
|
|
|
|
// Get example creator and github user |
|
|
// Get example creator and github user |
|
|
int authorIndex = TextFindIndex(exText, "Example contributed by "); // Author = index + 23 |
|
|
|
|
|
int authorGitIndex = TextFindIndex(exText, "(@"); // Author GitHub user = index + 2 |
|
|
|
|
|
if (authorIndex > 0) |
|
|
|
|
|
|
|
|
// NOTE: Using copyright line instead of "Example contributed by " because |
|
|
|
|
|
// most examples do not contain that line --> TODO: Review examples header formating? |
|
|
|
|
|
// Expected format: Copyright (c) <year_created>-<year_updated> <user_name> (@<user_github>) |
|
|
|
|
|
// Alternatives: Copyright (c) <year_created> <author_name> (@<user_github>) and <contrib_name> (@<contrib_user>) |
|
|
|
|
|
int copyrightIndex = TextFindIndex(exText, "Copyright (c) "); |
|
|
|
|
|
int yearStartIndex = copyrightIndex + 14; |
|
|
|
|
|
int yearEndIndex = TextFindIndex(exText + yearStartIndex, " "); |
|
|
|
|
|
int authorStartIndex = yearStartIndex + yearEndIndex + 1; |
|
|
|
|
|
int authorEndIndex = TextFindIndex(exText + authorStartIndex, " (@"); |
|
|
|
|
|
if (authorEndIndex != -1) // Github user also available |
|
|
{ |
|
|
{ |
|
|
int authorNameLen = 0; |
|
|
|
|
|
if (authorGitIndex > 0) authorNameLen = (authorGitIndex - 1) - (authorIndex + 23); |
|
|
|
|
|
else |
|
|
|
|
|
{ |
|
|
|
|
|
int authorNameEndIndex = TextFindIndex(exText + authorIndex, " and reviewed by Ramon Santamaria"); |
|
|
|
|
|
if (authorNameEndIndex == -1) authorNameEndIndex = TextFindIndex(exText + authorIndex, "\n"); |
|
|
|
|
|
|
|
|
|
|
|
authorNameLen = authorNameEndIndex - (authorIndex + 23); |
|
|
|
|
|
} |
|
|
|
|
|
strncpy(exInfo->author, exText + authorIndex + 23, authorNameLen); |
|
|
|
|
|
|
|
|
authorEndIndex += authorStartIndex; |
|
|
|
|
|
strncpy(exInfo->author, exText + authorStartIndex, authorEndIndex - authorStartIndex); |
|
|
|
|
|
|
|
|
|
|
|
// Get GitHub user |
|
|
|
|
|
int userStartIndex = authorEndIndex + 3; |
|
|
|
|
|
int userEndIndex = TextFindIndex(exText + userStartIndex, ")"); |
|
|
|
|
|
userEndIndex += userStartIndex; |
|
|
|
|
|
strncpy(exInfo->authorGitHub, exText + userStartIndex, userEndIndex - userStartIndex); |
|
|
} |
|
|
} |
|
|
else strcpy(exInfo->author, "<author_name>"); |
|
|
|
|
|
if (authorGitIndex > 0) |
|
|
|
|
|
|
|
|
else // GitHub user not found to set end, using '\n' |
|
|
{ |
|
|
{ |
|
|
int authorGitEndIndex = TextFindIndex(exText + authorGitIndex, ")"); |
|
|
|
|
|
if (authorGitEndIndex > 0) strncpy(exInfo->authorGitHub, exText + authorGitIndex + 2, authorGitEndIndex - (authorGitIndex + 2)); |
|
|
|
|
|
|
|
|
authorEndIndex = TextFindIndex(exText + authorStartIndex, "\n"); |
|
|
|
|
|
authorEndIndex += authorStartIndex; |
|
|
|
|
|
strncpy(exInfo->author, exText + authorStartIndex, authorEndIndex - authorStartIndex); |
|
|
} |
|
|
} |
|
|
else strcpy(exInfo->authorGitHub, "<user_github>"); |
|
|
|
|
|
|
|
|
|
|
|
// TODO: Verify copyright line |
|
|
|
|
|
// Copyright (c) <year_created>-<year_updated> <user_name> (@<user_github>) |
|
|
|
|
|
|
|
|
|
|
|
UnloadFileText(exText); |
|
|
UnloadFileText(exText); |
|
|
} |
|
|
} |
|
@ -1491,6 +1572,12 @@ rlExampleInfo *GetExampleInfo(const char *exFileName) |
|
|
return exInfo; |
|
|
return exInfo; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Unload example information |
|
|
|
|
|
static void UnloadExampleInfo(rlExampleInfo *exInfo) |
|
|
|
|
|
{ |
|
|
|
|
|
RL_FREE(exInfo); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// raylib example line info parser |
|
|
// raylib example line info parser |
|
|
// Parses following line format: core;core_basic_window;⭐️☆☆☆;1.0;1.0;"Ray";@raysan5 |
|
|
// Parses following line format: core;core_basic_window;⭐️☆☆☆;1.0;1.0;"Ray";@raysan5 |
|
|
static int ParseExampleInfoLine(const char *line, rlExampleInfo *entry) |
|
|
static int ParseExampleInfoLine(const char *line, rlExampleInfo *entry) |
|
@ -1531,7 +1618,7 @@ static int ParseExampleInfoLine(const char *line, rlExampleInfo *entry) |
|
|
if (tokens[5][0] == '"') tokens[5] += 1; |
|
|
if (tokens[5][0] == '"') tokens[5] += 1; |
|
|
if (tokens[5][strlen(tokens[5]) - 1] == '"') tokens[5][strlen(tokens[5]) - 1] = '\0'; |
|
|
if (tokens[5][strlen(tokens[5]) - 1] == '"') tokens[5][strlen(tokens[5]) - 1] = '\0'; |
|
|
strcpy(entry->author, tokens[5]); |
|
|
strcpy(entry->author, tokens[5]); |
|
|
strcpy(entry->authorGitHub, tokens[6]); |
|
|
|
|
|
|
|
|
strcpy(entry->authorGitHub, tokens[6] + 1); // Skip '@' |
|
|
|
|
|
|
|
|
return 1; |
|
|
return 1; |
|
|
} |
|
|
} |
|
|