9 Commits

3 changed files with 146 additions and 64 deletions

1
.gitignore vendored
View File

@ -114,3 +114,4 @@ docgen_tmp/
# Tools stuff # Tools stuff
tools/parser/raylib_parser tools/parser/raylib_parser
tools/rexm/VS2022 tools/rexm/VS2022
tools/rexm/rexm

View File

@ -58,16 +58,16 @@
* *
* raylib [<module>] example - <name> * raylib [<module>] example - <name>
* *
* Example complexity rating: [★☆??] ?/4 * Example complexity rating: [★☆☆☆] 1/4
* *
* Example originally created with raylib 5.5, last time updated with raylib 5.6-dev * Example originally created with raylib 5.5, last time updated with raylib 5.6
* *
* Example contributed by <user_name> (@<user_github>) and reviewed by Ramon Santamaria (@raysan5) * Example contributed by <author_name> (@<user_github>) and reviewed by Ramon Santamaria (@raysan5)
* *
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, * Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software * BSD-like license that allows static linking with closed source software
* *
* Copyright (c) <year_created>-<year_updated> <user_name> (@<user_github>) * Copyright (c) <year_created>-<year_updated> <author_name> (@<user_github>)
* *
********************************************************************************************/ ********************************************************************************************/

View File

@ -8,6 +8,7 @@
* - rename <old_examples_name> <new_example_name> * - rename <old_examples_name> <new_example_name>
* - remove <example_name> * - remove <example_name>
* - validate * - validate
* - update
* *
* Files involved in the processes: * Files involved in the processes:
* - raylib/examples/<category>/<category>_example_name.c * - raylib/examples/<category>/<category>_example_name.c
@ -58,6 +59,9 @@
#define LOG(...) #define LOG(...)
#endif #endif
#define REXM_MAX_EXAMPLES 512
#define REXM_MAX_EXAMPLE_CATEGORIES 8
#define REXM_MAX_BUFFER_SIZE (2*1024*1024) // 2MB #define REXM_MAX_BUFFER_SIZE (2*1024*1024) // 2MB
#define REXM_MAX_RESOURCE_PATHS 256 #define REXM_MAX_RESOURCE_PATHS 256
@ -84,11 +88,10 @@ typedef enum {
OP_RENAME = 3, // Rename existing example OP_RENAME = 3, // Rename existing example
OP_REMOVE = 4, // Remove existing example OP_REMOVE = 4, // Remove existing example
OP_VALIDATE = 5, // Validate examples, using [examples_list.txt] as main source by default OP_VALIDATE = 5, // Validate examples, using [examples_list.txt] as main source by default
OP_UPDATE = 6, // Validate and update required examples (as far as possible)
} rlExampleOperation; } rlExampleOperation;
#define MAX_EXAMPLE_CATEGORIES 8 static const char *exCategories[REXM_MAX_EXAMPLE_CATEGORIES] = { "core", "shapes", "textures", "text", "models", "shaders", "audio", "others" };
static const char *exCategories[MAX_EXAMPLE_CATEGORIES] = { "core", "shapes", "textures", "text", "models", "shaders", "audio", "others" };
// Paths required for examples management // Paths required for examples management
// TODO: Avoid hardcoding path values... // TODO: Avoid hardcoding path values...
@ -178,7 +181,7 @@ int main(int argc, char *argv[])
char cat[12] = { 0 }; char cat[12] = { 0 };
strncpy(cat, argv[2], catIndex); strncpy(cat, argv[2], catIndex);
bool catFound = false; bool catFound = false;
for (int i = 0; i < MAX_EXAMPLE_CATEGORIES; i++) for (int i = 0; i < REXM_MAX_EXAMPLE_CATEGORIES; i++)
{ {
if (TextIsEqual(cat, exCategories[i])) { catFound = true; break; } if (TextIsEqual(cat, exCategories[i])) { catFound = true; break; }
} }
@ -212,7 +215,7 @@ int main(int argc, char *argv[])
char cat[12] = { 0 }; char cat[12] = { 0 };
strncpy(cat, argv[2], catIndex); strncpy(cat, argv[2], catIndex);
bool catFound = false; bool catFound = false;
for (int i = 0; i < MAX_EXAMPLE_CATEGORIES; i++) for (int i = 0; i < REXM_MAX_EXAMPLE_CATEGORIES; i++)
{ {
if (TextIsEqual(cat, exCategories[i])) { catFound = true; break; } if (TextIsEqual(cat, exCategories[i])) { catFound = true; break; }
} }
@ -274,16 +277,23 @@ int main(int argc, char *argv[])
} }
else if (strcmp(argv[1], "validate") == 0) else if (strcmp(argv[1], "validate") == 0)
{ {
// Validate examples in collection // Validate examples in collection (report results)
// All examples in collection match all requirements on required files // All examples in collection match all files requirements
opCode = OP_VALIDATE; opCode = OP_VALIDATE;
} }
else if (strcmp(argv[1], "update") == 0)
{
// Validate and update examples in collection
// All examples in collection match all files requirements
opCode = OP_UPDATE;
}
} }
switch (opCode) switch (opCode)
{ {
case 1: // Create: New example from template case OP_CREATE: // Create: New example from template
{ {
// Create: raylib/examples/<category>/<category>_example_name.c // Create: raylib/examples/<category>/<category>_example_name.c
char *exText = LoadFileText(exTemplateFilePath); char *exText = LoadFileText(exTemplateFilePath);
@ -302,7 +312,7 @@ int main(int argc, char *argv[])
for (int i = 0; i < 6; i++) { MemFree(exTextUpdated[i]); exTextUpdated[i] = NULL; } for (int i = 0; i < 6; i++) { MemFree(exTextUpdated[i]); exTextUpdated[i] = NULL; }
UnloadFileText(exText); UnloadFileText(exText);
} }
case 2: // Add: Example from command-line input filename case OP_ADD: // Add: Example from command-line input filename
{ {
// Add: raylib/examples/<category>/<category>_example_name.c // Add: raylib/examples/<category>/<category>_example_name.c
if (opCode != 1) FileCopy(inFileName, TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName)); if (opCode != 1) FileCopy(inFileName, TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName));
@ -376,48 +386,102 @@ int main(int argc, char *argv[])
// Add example to the collection list, if not already there // Add example to the collection list, if not already there
// NOTE: Required format: shapes;shapes_basic_shapes;⭐️☆☆☆;1.0;4.2;"Ray";@raysan5 // NOTE: Required format: shapes;shapes_basic_shapes;⭐️☆☆☆;1.0;4.2;"Ray";@raysan5
//------------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------------
char *exColInfo = LoadFileText(exCollectionFilePath); char *exCollectionList = LoadFileText(exCollectionFilePath);
if (TextFindIndex(exColInfo, exName) == -1) // Example not found if (TextFindIndex(exCollectionList, exName) == -1) // Example not found
{ {
char *exColInfoUpdated = (char *)RL_CALLOC(2*1024*1024, 1); // Updated list copy, 2MB char *exCollectionListUpdated = (char *)RL_CALLOC(2*1024*1024, 1); // Updated list copy, 2MB
// Add example to the main list, by category // Add example to the main list, by category
// by default add it last in the category list // by default add it last in the category list
// NOTE: When populating to other files, lists are sorted by name // NOTE: When populating to other files, lists are sorted by name
int nextCatIndex = 0; int nextCategoryIndex = 0;
if (strcmp(exCategory, "core") == 0) nextCatIndex = 1; if (strcmp(exCategory, "core") == 0) nextCategoryIndex = 1;
else if (strcmp(exCategory, "shapes") == 0) nextCatIndex = 2; else if (strcmp(exCategory, "shapes") == 0) nextCategoryIndex = 2;
else if (strcmp(exCategory, "textures") == 0) nextCatIndex = 3; else if (strcmp(exCategory, "textures") == 0) nextCategoryIndex = 3;
else if (strcmp(exCategory, "text") == 0) nextCatIndex = 4; else if (strcmp(exCategory, "text") == 0) nextCategoryIndex = 4;
else if (strcmp(exCategory, "models") == 0) nextCatIndex = 5; else if (strcmp(exCategory, "models") == 0) nextCategoryIndex = 5;
else if (strcmp(exCategory, "shaders") == 0) nextCatIndex = 6; else if (strcmp(exCategory, "shaders") == 0) nextCategoryIndex = 6;
else if (strcmp(exCategory, "audio") == 0) nextCatIndex = 7; else if (strcmp(exCategory, "audio") == 0) nextCategoryIndex = 7;
else if (strcmp(exCategory, "others") == 0) nextCatIndex = -1; // Add to EOF else if (strcmp(exCategory, "others") == 0) nextCategoryIndex = -1; // Add to EOF
// TODO: 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
char *exText = LoadFileText(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName));
if (nextCatIndex == -1) rlExampleInfo exInfo = { 0 };
strcpy(exInfo.category, exCategory);
strcpy(exInfo.name, exName);
// Get example difficulty stars
char starsText[16] = { 0 };
int starsIndex = TextFindIndex(exText, "");
if (starsIndex > 0) strncpy(starsText, exText + starsIndex, 3*4); // NOTE: Every UTF-8 star are 3 bytes
else strcpy(starsText, "★☆☆☆");
// Get example create with raylib version
char verCreateText[4] = { 0 };
int verCreateIndex = TextFindIndex(exText, "created with raylib "); // Version = index + 20
if (verCreateIndex > 0) strncpy(verCreateText, exText + verCreateIndex + 20, 3);
else strncpy(verCreateText, RAYLIB_VERSION, 3); // Only pick MAJOR.MINOR
// Get example update with raylib version
char verUpdateText[4] = { 0 };
int verUpdateIndex = TextFindIndex(exText, "updated with raylib "); // Version = index + 20
if (verUpdateIndex > 0) strncpy(verUpdateText, exText + verUpdateIndex + 20, 3);
else strncpy(verUpdateText, RAYLIB_VERSION, 3); // Only pick MAJOR.MINOR
// 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)
{
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);
}
else strcpy(exInfo.author, "<author_name>");
if (authorGitIndex > 0)
{
int authorGitEndIndex = TextFindIndex(exText + authorGitIndex, ")");
if (authorGitEndIndex > 0) strncpy(exInfo.authorGitHub, exText + authorGitIndex + 2, authorGitEndIndex - (authorGitIndex + 2));
}
else strcpy(exInfo.author, "<user_github>");
// TODO: Verify copyright line
// Copyright (c) <year_created>-<year_updated> <user_name> (@<user_github>)
UnloadFileText(exText);
if (nextCategoryIndex == -1)
{ {
// Add example to collection at the EOF // Add example to collection at the EOF
int endIndex = (int)strlen(exColInfo); int endIndex = (int)strlen(exCollectionList);
memcpy(exColInfoUpdated, exColInfo, endIndex); memcpy(exCollectionListUpdated, exCollectionList, endIndex);
sprintf(exColInfoUpdated + endIndex, TextFormat("%s;%s;⭐️☆☆☆;6.0;6.0;\"Ray\";@raysan5\n", exCategory, exName)); sprintf(exCollectionListUpdated + endIndex, TextFormat("%s;%s;%s;%s;%s;\"%s\";@%s\n",
exInfo.category, exInfo.name, starsText, verCreateText, verUpdateText, exInfo.author, exInfo.authorGitHub));
} }
else else
{ {
// Add example to collection, at the end of the category list // Add example to collection, at the end of the category list
int catIndex = TextFindIndex(exColInfo, exCategories[nextCatIndex]); int categoryIndex = TextFindIndex(exCollectionList, exCategories[nextCategoryIndex]);
memcpy(exColInfoUpdated, exColInfo, catIndex); memcpy(exCollectionListUpdated, exCollectionList, categoryIndex);
int textWritenSize = sprintf(exColInfoUpdated + catIndex, TextFormat("%s;%s;⭐️☆☆☆;6.0;6.0;\"Ray\";@raysan5\n", exCategory, exName)); int textWritenSize = sprintf(exCollectionListUpdated + categoryIndex, TextFormat("%s;%s;%s;%s;%s;\"%s\";@%s\n",
memcpy(exColInfoUpdated + catIndex + textWritenSize, exColInfo + catIndex, strlen(exColInfo) - catIndex); exInfo.category, exInfo.name, starsText, verCreateText, verUpdateText, exInfo.author, exInfo.authorGitHub));
memcpy(exCollectionListUpdated + categoryIndex + textWritenSize, exCollectionList + categoryIndex, strlen(exCollectionList) - categoryIndex);
} }
SaveFileText(exCollectionFilePath, exColInfoUpdated); SaveFileText(exCollectionFilePath, exCollectionListUpdated);
RL_FREE(exColInfoUpdated); RL_FREE(exCollectionListUpdated);
} }
else LOG("WARNING: ADD: Example is already on the collection\n"); else LOG("WARNING: ADD: Example is already on the collection\n");
UnloadFileText(exColInfo); UnloadFileText(exCollectionList);
//------------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------------
// Update: Makefile, Makefile.Web, README.md, examples.js // Update: Makefile, Makefile.Web, README.md, examples.js
@ -445,8 +509,24 @@ int main(int argc, char *argv[])
// Compile to: raylib.com/examples/<category>/<category>_example_name.wasm // Compile to: raylib.com/examples/<category>/<category>_example_name.wasm
// Compile to: raylib.com/examples/<category>/<category>_example_name.js // Compile to: raylib.com/examples/<category>/<category>_example_name.js
//------------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------------
// TODO: Avoid platform-specific .BAT, not portable and it does not consider RESOURCES for Web properly, // TODO: Avoid platform-specific .BAT file
// Makefile.Web should be used... but it requires proper editing first! /*
SET RAYLIB_PATH=C:\GitHub\raylib
SET COMPILER_PATH=C:\raylib\w64devkit\bin
ENV_SET PATH=$(COMPILER_PATH)
SET MAKE=mingw32-make
$(MAKE) -f Makefile.Web shaders/shaders_deferred_render PLATFORM=$(PLATFORM) -B
//int putenv(char *string); // putenv takes a string of the form NAME=VALUE
//int setenv(const char *envname, const char *envval, int overwrite);
//int unsetenv(const char *name); //unset variable
putenv("RAYLIB_DIR=C:\\GitHub\\raylib");
putenv("PATH=%PATH%;C:\\raylib\\w64devkit\\bin");
setenv("RAYLIB_DIR", "C:\\GitHub\\raylib", 1);
unsetenv("RAYLIB_DIR");
getenv("RAYLIB_DIR");
system(TextFormat("make -f Makefile.Web %s/%s PLATFORM=PLATFORM_WEB -B", exCategory, exName));
*/
system(TextFormat("%s/build_example_web.bat %s/%s", exBasePath, exCategory, exName)); system(TextFormat("%s/build_example_web.bat %s/%s", exBasePath, exCategory, exName));
// Copy results to web side // Copy results to web side
@ -461,7 +541,7 @@ int main(int argc, char *argv[])
//------------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------------
} break; } break;
case 3: // Rename case OP_RENAME: // Rename
{ {
// NOTE: At this point provided values have been validated: // NOTE: At this point provided values have been validated:
// exName, exCategory, exRename, exRecategory // exName, exCategory, exRename, exRecategory
@ -536,27 +616,27 @@ int main(int argc, char *argv[])
TextFormat("%s/%s/%s.js", exWebPath, exRecategory, exRename)); TextFormat("%s/%s/%s.js", exWebPath, exRecategory, exRename));
} break; } break;
case 4: // Remove case OP_REMOVE: // Remove
{ {
// Remove example from collection for files update // Remove example from collection for files update
//------------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------------
char *exColInfo = LoadFileText(exCollectionFilePath); char *exCollectionList = LoadFileText(exCollectionFilePath);
int exIndex = TextFindIndex(exColInfo, TextFormat("%s;%s", exCategory, exName)); int exIndex = TextFindIndex(exCollectionList, TextFormat("%s;%s", exCategory, exName));
if (exIndex > 0) // Example found if (exIndex > 0) // Example found
{ {
char *exColInfoUpdated = (char *)RL_CALLOC(2*1024*1024, 1); // Updated list copy, 2MB char *exCollectionListUpdated = (char *)RL_CALLOC(2*1024*1024, 1); // Updated list copy, 2MB
memcpy(exColInfoUpdated, exColInfo, exIndex); memcpy(exCollectionListUpdated, exCollectionList, exIndex);
int lineLen = 0; int lineLen = 0;
for (int i = exIndex; (exColInfo[i] != '\n') && (exColInfo[i] != '\0'); i++) lineLen++; for (int i = exIndex; (exCollectionList[i] != '\n') && (exCollectionList[i] != '\0'); i++) lineLen++;
// Remove line and copy the rest next // Remove line and copy the rest next
memcpy(exColInfoUpdated + exIndex, exColInfo + exIndex + lineLen + 1, strlen(exColInfo) - exIndex - lineLen); memcpy(exCollectionListUpdated + exIndex, exCollectionList + exIndex + lineLen + 1, strlen(exCollectionList) - exIndex - lineLen);
SaveFileText(exCollectionFilePath, exColInfoUpdated); SaveFileText(exCollectionFilePath, exCollectionListUpdated);
RL_FREE(exColInfoUpdated); RL_FREE(exCollectionListUpdated);
} }
else LOG("WARNING: REMOVE: Example not found in the collection\n"); else LOG("WARNING: REMOVE: Example not found in the collection\n");
UnloadFileText(exColInfo); UnloadFileText(exCollectionList);
//------------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------------
// Remove: raylib/examples/<category>/resources/.. // Remove: raylib/examples/<category>/resources/..
@ -620,7 +700,8 @@ int main(int argc, char *argv[])
remove(TextFormat("%s/%s/%s.js", exWebPath, exCategory, exName)); remove(TextFormat("%s/%s/%s.js", exWebPath, exCategory, exName));
} break; } break;
case 5: // Validate case OP_VALIDATE: // Validate: report and actions
case OP_UPDATE:
{ {
// TODO: Validate examples in collection list [examples_list.txt] -> Source of truth! // TODO: Validate examples in collection list [examples_list.txt] -> Source of truth!
// Validate: raylib/examples/<category>/<category>_example_name.c -> File exists? // Validate: raylib/examples/<category>/<category>_example_name.c -> File exists?
@ -707,7 +788,7 @@ static int UpdateRequiredFiles(void)
memcpy(mkTextUpdated, mkText, mkListStartIndex); memcpy(mkTextUpdated, mkText, mkListStartIndex);
mkIndex = sprintf(mkTextUpdated + mkListStartIndex, "#EXAMPLES_LIST_START\n"); mkIndex = sprintf(mkTextUpdated + mkListStartIndex, "#EXAMPLES_LIST_START\n");
for (int i = 0; i < MAX_EXAMPLE_CATEGORIES; i++) for (int i = 0; i < REXM_MAX_EXAMPLE_CATEGORIES; i++)
{ {
mkIndex += sprintf(mkTextUpdated + mkListStartIndex + mkIndex, TextFormat("%s = \\\n", TextToUpper(exCategories[i]))); mkIndex += sprintf(mkTextUpdated + mkListStartIndex + mkIndex, TextFormat("%s = \\\n", TextToUpper(exCategories[i])));
@ -743,7 +824,7 @@ static int UpdateRequiredFiles(void)
mkwIndex = sprintf(mkwTextUpdated + mkwListStartIndex, "#EXAMPLES_LIST_START\n"); mkwIndex = sprintf(mkwTextUpdated + mkwListStartIndex, "#EXAMPLES_LIST_START\n");
// NOTE: We avoid the "others" category on web building // NOTE: We avoid the "others" category on web building
for (int i = 0; i < MAX_EXAMPLE_CATEGORIES - 1; i++) for (int i = 0; i < REXM_MAX_EXAMPLE_CATEGORIES - 1; i++)
{ {
mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, TextFormat("%s = \\\n", TextToUpper(exCategories[i]))); mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, TextFormat("%s = \\\n", TextToUpper(exCategories[i])));
@ -769,7 +850,7 @@ static int UpdateRequiredFiles(void)
mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, "audio: $(AUDIO)\n\n"); mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, "audio: $(AUDIO)\n\n");
// NOTE: We avoid the "others" category on web building // NOTE: We avoid the "others" category on web building
for (int i = 0; i < MAX_EXAMPLE_CATEGORIES - 1; i++) for (int i = 0; i < REXM_MAX_EXAMPLE_CATEGORIES - 1; i++)
{ {
mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, TextFormat("# Compile %s examples\n", TextToUpper(exCategories[i]))); mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, TextFormat("# Compile %s examples\n", TextToUpper(exCategories[i])));
@ -867,7 +948,7 @@ static int UpdateRequiredFiles(void)
mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, TextFormat("## EXAMPLES COLLECTION [TOTAL: %i]\n\n", exCollectionFullCount)); mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, TextFormat("## EXAMPLES COLLECTION [TOTAL: %i]\n\n", exCollectionFullCount));
// NOTE: We keep a global examples counter // NOTE: We keep a global examples counter
for (int i = 0; i < MAX_EXAMPLE_CATEGORIES; i++) for (int i = 0; i < REXM_MAX_EXAMPLE_CATEGORIES; i++)
{ {
int exCollectionCount = 0; int exCollectionCount = 0;
rlExampleInfo *exCollection = LoadExamplesData(exCollectionFilePath, exCategories[i], false, &exCollectionCount); rlExampleInfo *exCollection = LoadExamplesData(exCollectionFilePath, exCategories[i], false, &exCollectionCount);
@ -970,7 +1051,7 @@ static int UpdateRequiredFiles(void)
jsIndex += sprintf(jsTextUpdated + jsListStartIndex + jsIndex, " var exampleData = [\n"); jsIndex += sprintf(jsTextUpdated + jsListStartIndex + jsIndex, " var exampleData = [\n");
// NOTE: We avoid "others" category // NOTE: We avoid "others" category
for (int i = 0; i < MAX_EXAMPLE_CATEGORIES - 1; i++) for (int i = 0; i < REXM_MAX_EXAMPLE_CATEGORIES - 1; i++)
{ {
int exCollectionCount = 0; int exCollectionCount = 0;
rlExampleInfo *exCollection = LoadExamplesData(exCollectionFilePath, exCategories[i], false, &exCollectionCount); rlExampleInfo *exCollection = LoadExamplesData(exCollectionFilePath, exCategories[i], false, &exCollectionCount);