| @ -0,0 +1,349 @@ | |||
| /******************************************************************************************* | |||
| * | |||
| * rexm [raylib examples manager] - A simple command-line tool to manage raylib examples | |||
| * | |||
| * Supported processes: | |||
| * - create <new_example_name> | |||
| * - add <example_name> | |||
| * - rename <old_examples_name> <new_example_name> | |||
| * - remove <example_name> | |||
| * | |||
| * Files involved in the processes: | |||
| * - raylib/examples/<category>/<category>_example_name.c | |||
| * - raylib/examples/<category>/<category>_example_name.png | |||
| * - raylib/examples/<category>/resources/.. | |||
| * - raylib/examples/Makefile | |||
| * - raylib/examples/Makefile.Web | |||
| * - raylib/examples/README.md | |||
| * - raylib/projects/VS2022/examples/<category>_example_name.vcxproj | |||
| * - raylib/projects/VS2022/raylib.sln | |||
| * - raylib.com/common/examples.js | |||
| * - raylib.com/examples/<category>/<category>_example_name.html | |||
| * - raylib.com/examples/<category>/<category>_example_name.data | |||
| * - raylib.com/examples/<category>/<category>_example_name.wasm | |||
| * - raylib.com/examples/<category>/<category>_example_name.js | |||
| * | |||
| * LICENSE: zlib/libpng | |||
| * | |||
| * Copyright (c) 2025 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. | |||
| * | |||
| **********************************************************************************************/ | |||
| #include "raylib.h" | |||
| #include <stdlib.h> | |||
| #include <stdio.h> // Required for: rename(), remove() | |||
| #include <string.h> // Required for: strcmp(), strcpy() | |||
| #define SUPPORT_LOG_INFO | |||
| #if defined(SUPPORT_LOG_INFO) && defined(_DEBUG) | |||
| #define LOG(...) printf(__VA_ARGS__) | |||
| #else | |||
| #define LOG(...) | |||
| #endif | |||
| //---------------------------------------------------------------------------------- | |||
| // Types and Structures Definition | |||
| //---------------------------------------------------------------------------------- | |||
| // Example management operations | |||
| typedef enum { | |||
| OP_NONE = 0, | |||
| OP_CREATE = 1, | |||
| OP_ADD = 2, | |||
| OP_RENAME = 3, | |||
| OP_REMOVE = 4 | |||
| } ExampleOperation; | |||
| //---------------------------------------------------------------------------------- | |||
| // Module specific functions declaration | |||
| //---------------------------------------------------------------------------------- | |||
| static int FileTextReplace(const char *fileName, const char *textLookUp, const char *textReplace); | |||
| static int FileCopy(const char *srcPath, const char *dstPath); | |||
| static int FileRename(const char *fileName, const char *fileRename); // TODO: Implement, make sure to deal with paths moving | |||
| static int FileRemove(const char *fileName); // TODO: Implement | |||
| //------------------------------------------------------------------------------------ | |||
| // Program main entry point | |||
| //------------------------------------------------------------------------------------ | |||
| int main(int argc, char *argv[]) | |||
| { | |||
| // Examples path for building | |||
| // TODO: Avoid hardcoding path values... | |||
| char *exBasePath = "C:/GitHub/raylib/examples"; | |||
| char *exWebPath = "C:/GitHub/raylib.com/examples"; | |||
| char *exTemplateFilePath = "C:/GitHub/raylib/examples/examples_template.c"; | |||
| char inFileName[1024] = { 0 }; // Example input filename | |||
| char exName[1024] = { 0 }; // Example name, without extension: core_basic_window | |||
| char exCategory[32] = { 0 }; // Example category: core | |||
| char exRename[1024] = { 0 }; // Example re-name, without extension | |||
| char exPath[1024] = { 0 }; // Example path -NOT USED- | |||
| char exFullPath[1024] = { 0 }; // Example full path -NOT USED- | |||
| int opCode = OP_NONE; // Operation code: 0-None(Help), 1-Create, 2-Add, 3-Rename, 4-Remove | |||
| // Command-line usage mode | |||
| //-------------------------------------------------------------------------------------- | |||
| if (argc > 1) | |||
| { | |||
| // Supported commands: | |||
| // help : Provides command-line usage information (default) | |||
| // create <new_example_name> : Creates an empty example, from internal template | |||
| // add <example_name> : Add existing example, category extracted from name | |||
| // rename <old_examples_name> <new_example_name> : Rename an existing example | |||
| // remove <example_name> : Remove an existing example | |||
| if (strcmp(argv[1], "create") == 0) | |||
| { | |||
| // Check for valid upcoming argument | |||
| if (argc == 2) LOG("WARNING: No filename provided to create\n"); | |||
| else if (argc > 3) LOG("WARNING: Too many arguments provided\n"); | |||
| else | |||
| { | |||
| // TODO: Additional security checks for file name? | |||
| strcpy(inFileName, argv[2]); // Register filename for creation | |||
| opCode = 1; | |||
| } | |||
| } | |||
| else if (strcmp(argv[1], "add") == 0) | |||
| { | |||
| // Check for valid upcoming argument | |||
| if (argc == 2) LOG("WARNING: No filename provided to create\n"); | |||
| else if (argc > 3) LOG("WARNING: Too many arguments provided\n"); | |||
| else | |||
| { | |||
| if (IsFileExtension(argv[2], ".c")) // Check for valid file extension: input | |||
| { | |||
| // TODO: Parse category name from filename provided! | |||
| strcpy(inFileName, argv[2]); // Register filename for creation | |||
| opCode = 2; | |||
| } | |||
| else LOG("WARNING: Input file extension not recognized (.c)\n"); | |||
| } | |||
| } | |||
| else if (strcmp(argv[1], "rename") == 0) | |||
| { | |||
| if (argc == 2) LOG("WARNING: No filename provided to create\n"); | |||
| else if (argc == 3) LOG("WARNING: No enough arguments provided\n"); | |||
| else if (argc > 4) LOG("WARNING: Too many arguments provided\n"); | |||
| else | |||
| { | |||
| // TODO: Register exName, exCategory and exRename | |||
| opCode = 3; | |||
| } | |||
| } | |||
| else if (strcmp(argv[1], "remove") == 0) | |||
| { | |||
| // Check for valid upcoming argument | |||
| if (argc == 2) LOG("WARNING: No filename provided to create\n"); | |||
| else if (argc > 3) LOG("WARNING: Too many arguments provided\n"); | |||
| else | |||
| { | |||
| strcpy(inFileName, argv[2]); // Register filename for removal | |||
| opCode = 4; | |||
| } | |||
| } | |||
| } | |||
| switch (opCode) | |||
| { | |||
| case 1: // Create: New example from template | |||
| { | |||
| // Copy template file as new example | |||
| FileCopy(exTemplateFilePath, TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName)); | |||
| } | |||
| case 2: // Add: Example from command-line input filename | |||
| { | |||
| if ((opCode != 1) && FileExists(inFileName)) | |||
| { | |||
| FileCopy(inFileName, TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName)); | |||
| } | |||
| // Generate all required files | |||
| //-------------------------------------------------------------------------------- | |||
| // Create: raylib/examples/<category>/<category>_example_name.c | |||
| // Create: raylib/examples/<category>/<category>_example_name.png | |||
| FileCopy("C:/GitHub/raylib/examples/examples_template.png", | |||
| TextFormat("%s/%s/%s.png", exBasePath, exCategory, exName)); // To be updated manually! | |||
| // Copy: raylib/examples/<category>/resources/*.* ---> To be updated manually! | |||
| // TODO: Update the required files to add new example in the required position (ordered by category and name), | |||
| // it could require some logic to make it possible... | |||
| // Edit: raylib/examples/Makefile --> Add new example | |||
| // Edit: raylib/examples/Makefile.Web --> Add new example | |||
| // Edit: raylib/examples/README.md --> Add new example | |||
| // Create: raylib/projects/VS2022/examples/<category>_example_name.vcxproj | |||
| // Edit: raylib/projects/VS2022/raylib.sln --> Add new example | |||
| // Edit: raylib.com/common/examples.js --> Add new example | |||
| // Compile to: raylib.com/examples/<category>/<category>_example_name.html | |||
| // Compile to: raylib.com/examples/<category>/<category>_example_name.data | |||
| // Compile to: raylib.com/examples/<category>/<category>_example_name.wasm | |||
| // Compile to: raylib.com/examples/<category>/<category>_example_name.js | |||
| // Recompile example (on raylib side) | |||
| // NOTE: Tools requirements: emscripten, w64devkit | |||
| system(TextFormat("%s/../build_example_web.bat %s\%s", exBasePath, exCategory, exName)); | |||
| // Copy results to web side | |||
| FileCopy(TextFormat("%s/%s/%s.html", exBasePath, exCategory, exName), | |||
| TextFormat("%s/%s/%s.html", exWebPath, exCategory, exName)); | |||
| FileCopy(TextFormat("%s/%s/%s.data", exBasePath, exCategory, exName), | |||
| TextFormat("%s/%s/%s.data", exWebPath, exCategory, exName)); | |||
| FileCopy(TextFormat("%s/%s/%s.wasm", exBasePath, exCategory, exName), | |||
| TextFormat("%s/%s/%s.wasm", exWebPath, exCategory, exName)); | |||
| FileCopy(TextFormat("%s/%s/%s.js", exBasePath, exCategory, exName), | |||
| TextFormat("%s/%s/%s.js", exWebPath, exCategory, exName)); | |||
| } break; | |||
| case 3: // Rename | |||
| { | |||
| // Rename all required files | |||
| rename(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName), | |||
| TextFormat("%s/%s/%s.c", exBasePath, exCategory, exRename)); | |||
| rename(TextFormat("%s/%s/%s.png", exBasePath, exCategory, exName), | |||
| TextFormat("%s/%s/%s.png", exBasePath, exCategory, exRename)); | |||
| FileTextReplace(TextFormat("%s/Makefile", exBasePath), exName, exRename); | |||
| FileTextReplace(TextFormat("%s/Makefile.Web", exBasePath), exName, exRename); | |||
| FileTextReplace(TextFormat("%s/README.md", exBasePath), exName, exRename); | |||
| rename(TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exName), | |||
| TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exRename)); | |||
| FileTextReplace(TextFormat("%s/../projects/VS2022/raylib.sln", exBasePath), exName, exRename); | |||
| // Remove old web compilation | |||
| FileTextReplace(TextFormat("%s/../common/examples.js", exWebPath), exName, exRename); | |||
| remove(TextFormat("%s/%s/%s.html", exWebPath, exCategory, exName)); | |||
| remove(TextFormat("%s/%s/%s.data", exWebPath, exCategory, exName)); | |||
| remove(TextFormat("%s/%s/%s.wasm", exWebPath, exCategory, exName)); | |||
| remove(TextFormat("%s/%s/%s.js", exWebPath, exCategory, exName)); | |||
| // Recompile example (on raylib side) | |||
| // NOTE: Tools requirements: emscripten, w64devkit | |||
| system(TextFormat("%s/../build_example_web.bat %s\%s", exBasePath, exCategory, exName)); | |||
| // Copy results to web side | |||
| FileCopy(TextFormat("%s/%s/%s.html", exBasePath, exCategory, exName), | |||
| TextFormat("%s/%s/%s.html", exWebPath, exCategory, exName)); | |||
| FileCopy(TextFormat("%s/%s/%s.data", exBasePath, exCategory, exName), | |||
| TextFormat("%s/%s/%s.data", exWebPath, exCategory, exName)); | |||
| FileCopy(TextFormat("%s/%s/%s.wasm", exBasePath, exCategory, exName), | |||
| TextFormat("%s/%s/%s.wasm", exWebPath, exCategory, exName)); | |||
| FileCopy(TextFormat("%s/%s/%s.js", exBasePath, exCategory, exName), | |||
| TextFormat("%s/%s/%s.js", exWebPath, exCategory, exName)); | |||
| } break; | |||
| case 4: // Remove | |||
| { | |||
| // TODO: Remove and update all required files... | |||
| } break; | |||
| default: // Help | |||
| { | |||
| // Supported commands: | |||
| // help : Provides command-line usage information | |||
| // create <new_example_name> : Creates an empty example, from internal template | |||
| // add <example_name> : Add existing example, category extracted from name | |||
| // rename <old_examples_name> <new_example_name> : Rename an existing example | |||
| // remove <example_name> : Remove an existing example | |||
| printf("\n////////////////////////////////////////////////////////////////////////////////////////////\n"); | |||
| printf("// //\n"); | |||
| printf("// rem [raylib examples manager] - A simple command-line tool to manage raylib examples //\n"); | |||
| printf("// powered by raylib v5.6-dev //\n"); | |||
| printf("// //\n"); | |||
| printf("// Copyright (c) 2025 Ramon Santamaria (@raysan5) //\n"); | |||
| printf("// //\n"); | |||
| printf("////////////////////////////////////////////////////////////////////////////////////////////\n\n"); | |||
| printf("USAGE:\n\n"); | |||
| printf(" > rem help|create|add|rename|remove <example_name> [<example_rename>]\n"); | |||
| printf("\nOPTIONS:\n\n"); | |||
| printf(" help : Provides command-line usage information\n"); | |||
| printf(" create <new_example_name> : Creates an empty example, from internal template\n"); | |||
| printf(" add <example_name> : Add existing example, category extracted from name\n"); | |||
| printf(" Supported categories: core, shapes, textures, text, models\n\n"); | |||
| printf(" rename <old_examples_name> <new_example_name> : Rename an existing example\n"); | |||
| printf(" remove <example_name> : Remove an existing example\n\n"); | |||
| printf("\nEXAMPLES:\n\n"); | |||
| printf(" > rem rename core_basic_window core_cool_window\n"); | |||
| printf(" Renames and updates example <core_basic_window> to <core_cool_window>\n\n"); | |||
| printf(" > rem add shapes_custom_stars\n"); | |||
| printf(" Add and updates new example provided <shapes_custom_stars>\n\n"); | |||
| } break; | |||
| } | |||
| return 0; | |||
| } | |||
| //---------------------------------------------------------------------------------- | |||
| // Module specific functions definition | |||
| //---------------------------------------------------------------------------------- | |||
| static int FileTextReplace(const char *fileName, const char *textLookUp, const char *textReplace) | |||
| { | |||
| int result = 0; | |||
| char *fileText = NULL; | |||
| char *fileTextUpdated = { 0 }; | |||
| fileText = LoadFileText(fileName); | |||
| fileTextUpdated = TextReplace(fileText, textLookUp, textReplace); | |||
| result = SaveFileText(fileName, fileTextUpdated); | |||
| MemFree(fileTextUpdated); | |||
| UnloadFileText(fileText); | |||
| return result; | |||
| } | |||
| static int FileCopy(const char *srcPath, const char *dstPath) | |||
| { | |||
| int result = 0; | |||
| int srcDataSize = 0; | |||
| unsigned char *srcFileData = LoadFileData(srcPath, &srcDataSize); | |||
| if ((srcFileData != NULL) && (srcDataSize > 0)) result = SaveFileData(dstPath, srcFileData, srcDataSize); | |||
| UnloadFileData(srcFileData); | |||
| return result; | |||
| } | |||
| static int FileRename(const char *fileName, const char *fileRename) | |||
| { | |||
| int result = 0; | |||
| // TODO: Make sure to deal with paths properly for file moving if required | |||
| if (FileExists(fileName)) rename(fileName, TextFormat("%s/%s", GetDirectoryPath(fileName), fileRename)); | |||
| return result; | |||
| } | |||
| static int FileRemove(const char *fileName) | |||
| { | |||
| int result = 0; | |||
| if (FileExists(fileName)) remove(fileName); | |||
| return result; | |||
| } | |||