2
0
Эх сурвалжийг харах

ADDED: Some useful functions for examples info loading, using `examples_list`

Ray 1 сар өмнө
parent
commit
9477838193
1 өөрчлөгдсөн 240 нэмэгдсэн , 54 устгасан
  1. 240 54
      examples/rexm.c

+ 240 - 54
examples/rexm.c

@@ -7,6 +7,7 @@
 *    - add <example_name>
 *    - rename <old_examples_name> <new_example_name>
 *    - remove <example_name>
+*    - validate
 *
 *   Files involved in the processes:
 *    - raylib/examples/<category>/<category>_example_name.c
@@ -60,22 +61,50 @@
 //----------------------------------------------------------------------------------
 // Types and Structures Definition
 //----------------------------------------------------------------------------------
+// raylib example info struct
+typedef struct {
+    char category[16];
+    char name[64];
+    char stars;
+    float verCreated;
+    float verUpdated;
+    char author[64];
+    char authorGitHub[32];
+} rlExampleInfo;
+
 // Example management operations
 typedef enum {
-    OP_NONE     = 0,
-    OP_CREATE   = 1,
-    OP_ADD      = 2,
-    OP_RENAME   = 3,
-    OP_REMOVE   = 4
-} ExampleOperation;
+    OP_NONE     = 0,    // No process to do
+    OP_CREATE   = 1,    // Create new example, using default template
+    OP_ADD      = 2,    // Add existing examples (hopefully following template)
+    OP_RENAME   = 3,    // Rename existing example
+    OP_REMOVE   = 4,    // Remove existing example
+    OP_VALIDATE = 5,    // Validate examples, using [examples_list.txt] as main source by default
+} rlExampleOperation;
 
 //----------------------------------------------------------------------------------
 // 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
+static int FileRename(const char *fileName, const char *fileRename);
+static int FileRemove(const char *fileName);
+
+// Load examples collection information
+static rlExampleInfo *LoadExamplesData(const char *fileName, int *exCount);
+static void UnloadExamplesData(rlExampleInfo *exInfo);
+
+// Get text lines (by line-breaks '\n')
+// WARNING: It does not copy text data, just returns line pointers 
+static const char **GetTextLines(const char *text, int *count);
+
+// raylib example line info parser
+// Parses following line format: core/core_basic_window;⭐️☆☆☆;1.0;1.0;"Ray"/@raysan5
+static int ParseExampleInfoLine(const char *line, rlExampleInfo *entry);
+
+// Sort array of strings by name
+// WARNING: items[] pointers are reorganized
+static void SortStringsByName(char **items, int count);
 
 //------------------------------------------------------------------------------------
 // Program main entry point
@@ -87,15 +116,14 @@ int main(int argc, char *argv[])
     char *exBasePath = "C:/GitHub/raylib/examples";
     char *exWebPath = "C:/GitHub/raylib.com/examples";
     char *exTemplateFilePath = "C:/GitHub/raylib/examples/examples_template.c";
-    
+    char *exCollectionList = "C:/GitHub/raylib/examples/examples_list.txt";
+
     char inFileName[1024] = { 0 };  // Example input filename
 
-    char exName[1024] = { 0 };      // Example name, without extension: core_basic_window
+    char exName[64] = { 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-
-    
+    char exRename[64] = { 0 };      // Example re-name, without extension
+
     int opCode = OP_NONE;           // Operation code: 0-None(Help), 1-Create, 2-Add, 3-Rename, 4-Remove
 
     // Command-line usage mode
@@ -108,6 +136,7 @@ int main(int argc, char *argv[])
         //    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
+        //    validate                      : Validate examples collection
         if (strcmp(argv[1], "create") == 0)
         {
             // Check for valid upcoming argument
@@ -116,7 +145,7 @@ int main(int argc, char *argv[])
             else
             {
                 // TODO: Additional security checks for file name?
-                
+
                 strcpy(inFileName, argv[2]); // Register filename for creation
                 opCode = 1;
             }
@@ -131,7 +160,7 @@ int main(int argc, char *argv[])
                 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;
                 }
@@ -141,17 +170,11 @@ int main(int argc, char *argv[])
         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"); All the documentation says 3 args but I don't mind being wrong
-            else if (argc > 3) LOG("WARNING: Too many arguments provided\n");
+            else if (argc == 3) LOG("WARNING: No enough arguments provided\n");
+            else if (argc > 4) LOG("WARNING: Too many arguments provided\n");
             else
             {
-                strcpy(exName, argv[2]);
-                for (int index = 0; index < 32; index++)
-                {
-                    if (exName[index] == '_') break;
-                    exCategory[index] = exName[index];
-                }
-                strcpy(exRename, argv[3]);
+                // TODO: Register exName, exCategory and exRename
 
                 opCode = 3;
             }
@@ -167,8 +190,12 @@ int main(int argc, char *argv[])
                 opCode = 4;
             }
         }
+        else if (strcmp(argv[1], "validate") == 0)
+        {
+             opCode = 5;
+        }
     }
-    
+
     switch (opCode)
     {
         case 1:     // Create: New example from template
@@ -181,33 +208,33 @@ int main(int argc, char *argv[])
             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", 
+            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));
@@ -225,19 +252,19 @@ int main(int argc, char *argv[])
         case 3:     // Rename
         {
             // Rename all required files
-            rename(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName), 
+            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), 
+            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), 
+
+            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));
@@ -263,6 +290,25 @@ int main(int argc, char *argv[])
         {
             // TODO: Remove and update all required files...
 
+        } break;
+        case 5:     // Validate
+        {
+            // TODO: Validate examples collection against [examples_list.txt]
+            
+            // Validate: raylib/examples/<category>/<category>_example_name.c
+            // Validate: raylib/examples/<category>/<category>_example_name.png
+            // Validate: raylib/examples/<category>/resources/.. -> Not possible for now...
+            // Validate: raylib/examples/Makefile
+            // Validate: raylib/examples/Makefile.Web
+            // Validate: raylib/examples/README.md
+            // Validate: raylib/projects/VS2022/examples/<category>_example_name.vcxproj
+            // Validate: raylib/projects/VS2022/raylib.sln
+            // Validate: raylib.com/common/examples.js
+            // Validate: raylib.com/examples/<category>/<category>_example_name.html
+            // Validate: raylib.com/examples/<category>/<category>_example_name.data
+            // Validate: raylib.com/examples/<category>/<category>_example_name.wasm
+            // Validate: raylib.com/examples/<category>/<category>_example_name.js
+            
         } break;
         default:    // Help
         {
@@ -272,7 +318,7 @@ int main(int argc, char *argv[])
             //    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("// rexm [raylib examples manager] - A simple command-line tool to manage raylib examples  //\n");
@@ -299,57 +345,197 @@ int main(int argc, char *argv[])
             printf("        Renames and updates example <core_basic_window> to <core_cool_window>\n\n");
         } break;
     }
-    
+
     return 0;
 }
 
 //----------------------------------------------------------------------------------
 // Module specific functions definition
 //----------------------------------------------------------------------------------
+// Load examples collection information
+static rlExampleInfo *LoadExamplesData(const char *fileName, int *exCount)
+{
+    #define MAX_EXAMPLES_INFO   256
+    
+    *exCount = 0;
+    rlExampleInfo *exInfo = (rlExampleInfo *)RL_CALLOC(MAX_EXAMPLES_INFO, sizeof(rlExampleInfo));
+    
+    const char *text = LoadFileText(fileName);
+    
+    if (text != NULL)
+    {
+        int lineCount = 0;
+        const char **linePtrs = GetTextLines(text, &lineCount);
+        
+        for (int i = 0; i < lineCount; i++)
+        {
+            // Basic validation for lines start categories
+            if ((linePtrs[i][0] != '#') && 
+               ((linePtrs[i][0] == 'c') ||      // core
+                (linePtrs[i][0] == 's') ||      // shapes, shaders
+                (linePtrs[i][0] == 't') ||      // textures, text
+                (linePtrs[i][0] == 'm') ||      // models
+                (linePtrs[i][0] == 'a') ||      // audio
+                (linePtrs[i][0] == 'o')))       // others
+            {
+                if (ParseExampleInfoLine(linePtrs[i], &exInfo[*exCount]) == 0) *exCount += 1;
+            }
+        }
+    }
+    
+    return exInfo;
+}
+
+// Unload examples collection data
+static void UnloadExamplesData(rlExampleInfo *exInfo)
+{
+    RL_FREE(exInfo);
+}
+
+// Replace text in an existing file
 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);
     
+    if (FileExists(fileName))
+    {
+        fileText = LoadFileText(fileName);
+        fileTextUpdated = TextReplace(fileText, textLookUp, textReplace);
+        result = SaveFileText(fileName, fileTextUpdated);
+        MemFree(fileTextUpdated);
+        UnloadFileText(fileText);
+    }
+
     return result;
 }
 
+// Copy file from one path to another
+// WARNING: Destination path must exist
 static int FileCopy(const char *srcPath, const char *dstPath)
 {
     int result = 0;
     int srcDataSize = 0;
     unsigned char *srcFileData = LoadFileData(srcPath, &srcDataSize);
     
+    // TODO: Create required paths if they do not exist
+
     if ((srcFileData != NULL) && (srcDataSize > 0)) result = SaveFileData(dstPath, srcFileData, srcDataSize);
 
     UnloadFileData(srcFileData);
-    
+
     return result;
 }
 
+// Rename file (if exists)
+// NOTE: Only rename file name required, not full path
 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;
 }
 
+// Remove file (if exists)
 static int FileRemove(const char *fileName)
 {
     int result = 0;
-    
+
     if (FileExists(fileName)) remove(fileName);
 
     return result;
 }
+
+// Get text lines (by line-breaks '\n')
+// WARNING: It does not copy text data, just returns line pointers 
+static const char **GetTextLines(const char *text, int *count)
+{
+    #define MAX_TEXT_LINE_PTRS   128
+
+    static const char *linePtrs[MAX_TEXT_LINE_PTRS] = { 0 };
+    for (int i = 0; i < MAX_TEXT_LINE_PTRS; i++) linePtrs[i] = NULL;    // Init NULL pointers to substrings
+
+    int textSize = (int)strlen(text);
+
+    linePtrs[0] = text;
+    int len = 0;
+    *count = 1;
+
+    for (int i = 0, k = 0; (i < textSize) && (*count < MAX_TEXT_LINE_PTRS); i++)
+    {
+        if (text[i] == '\n')
+        {
+            k++;
+            linePtrs[k] = &text[i + 1]; // WARNING: next value is valid?
+            len = 0;
+            *count += 1;
+        }
+        else len++;
+    }
+
+    return linePtrs;
+}
+
+// raylib example line info parser
+// Parses following line format: core/core_basic_window;⭐️☆☆☆;1.0;1.0;"Ray"/@raysan5
+static int ParseExampleInfoLine(const char *line, rlExampleInfo *entry)
+{
+    #define MAX_EXAMPLE_INFO_LINE_LEN   512
+    
+    char temp[MAX_EXAMPLE_INFO_LINE_LEN] = { 0 };
+    strncpy(temp, line, MAX_EXAMPLE_INFO_LINE_LEN); // WARNING: Copy is needed because strtok() modifies string, adds '\0' 
+    temp[MAX_EXAMPLE_INFO_LINE_LEN - 1] = '\0'; // Ensure null termination
+    
+    int tokenCount = 0;
+    char **tokens = TextSplit(line, ';', &tokenCount);
+
+    // Get category and name
+    strncpy(entry->category, tokens[0], sizeof(entry->category));
+    strncpy(entry->name, tokens[1], sizeof(entry->name));
+
+    // Parsing stars
+    // NOTE: Counting the unicode char occurrences: ⭐️
+    const char *ptr = tokens[2];
+    while (*ptr) 
+    {
+        if (((unsigned char)ptr[0] == 0xE2) && 
+            ((unsigned char)ptr[1] == 0xAD) && 
+            ((unsigned char)ptr[2] == 0x90))
+        {
+            entry->stars++;
+            ptr += 3; // Advance past multibyte character
+        }
+        else ptr++;
+    }
+
+    // Get raylib creation/update versions
+    entry->verCreated = strtof(tokens[3], NULL);
+    entry->verUpdated = strtof(tokens[4], NULL);
+
+    // Get author and github
+    char *quote1 = strchr(tokens[5], '"');
+    char *quote2 = quote1? strchr(quote1 + 1, '"') : NULL;
+    if (quote1 && quote2) strncpy(entry->author, quote1 + 1, sizeof(entry->author));
+    strncpy(entry->authorGitHub, tokens[6], sizeof(entry->authorGitHub));
+
+    return 1;
+}
+
+// Text compare, required for qsort() function
+static int SortTextCompare(const void *a, const void *b)
+{
+    const char *str1 = *(const char **)a;
+    const char *str2 = *(const char **)b;
+    
+    return strcmp(str1, str2);
+}
+
+// Sort array of strings by name
+// WARNING: items[] pointers are reorganized
+static void SortStringsByName(char **items, int count)
+{
+    qsort(items, count, sizeof(char *), SortTextCompare);
+}