Browse Source

UPDATE: Added resources scanning from examples for automated copying

Ray 5 days ago
parent
commit
bc0f93baea
1 changed files with 147 additions and 12 deletions
  1. 147 12
      examples/rexm.c

+ 147 - 12
examples/rexm.c

@@ -58,6 +58,10 @@
     #define LOG(...)
 #endif
 
+#define REXM_MAX_BUFFER_SIZE    (2*1024*1024)      // 2MB
+
+#define REXM_MAX_RESOURCE_PATHS 256
+
 //----------------------------------------------------------------------------------
 // Types and Structures Definition
 //----------------------------------------------------------------------------------
@@ -125,6 +129,12 @@ static int ParseExampleInfoLine(const char *line, rlExampleInfo *entry);
 // WARNING: items[] pointers are reorganized
 static void SortExampleByName(rlExampleInfo *items, int count);
 
+// Scan resource paths in example file
+static char **ScanExampleResources(const char *filePath, int *resPathCount);
+
+// Clear resource paths scanned
+static void ClearExampleResources(char **resPaths);
+
 //------------------------------------------------------------------------------------
 // Program main entry point
 //------------------------------------------------------------------------------------
@@ -296,17 +306,68 @@ int main(int argc, char *argv[])
             // Create: raylib/examples/<category>/<category>_example_name.png
             FileCopy(exTemplateScreenshot, TextFormat("%s/%s/%s.png", exBasePath, exCategory, exName)); // WARNING: To be updated manually!
 
-            // Copy: raylib/examples/<category>/resources/...  // WARNING: To be updated manually!
-            // IDEA: Example to be added could be provided as a .zip, containing resources!
-
-            // TODO: Copy provided resources to respective directories
-            // Possible strategy:
-            //  1. Scan code file for resources paths -> Resources list
-            //    Look for specific text: '.png"'
-            //    Look for full path, previous '"'
-            //    Be careful with shaders: '.vs"', '.fs"' -> Reconstruct path manually?
-            //  2. Verify paths: resource files exist
-            //  3. Copy files to required resource dir
+            // Copy: raylib/examples/<category>/resources/...
+            // -----------------------------------------------------------------------------------------
+            // Scan resources used in example to copy
+            int resPathCount = 0;
+            char **resPaths = ScanExampleResources(TextFormat("%s/%s/%s.png", exBasePath, exCategory, exName), &resPathCount);
+
+            if (resPathCount > 0)
+            {
+                for (int r = 0; r < resPathCount; r++)
+                {
+                    // WARNING: Special case to consider: shaders, resource paths could use conditions: "glsl%i"
+                    // In this case, multiple resources are required: glsl100, glsl120, glsl330
+                    if (TextFindIndex(resPaths[r], "glsl%i") > -1)
+                    {
+                        int glslVer[3] = { 100, 120, 330 };
+
+                        for (int v = 0; v < 3; v++)
+                        {
+                            char *resPathUpdated = TextReplace(resPaths[r], "glsl%i", TextFormat("glsl%i", glslVer[v]));
+
+                            LOG("INFO: Example resource required: %s\n", resPathUpdated);
+
+                            if (FileExists(TextFormat("%s/%s", GetDirectoryPath(inFileName), resPathUpdated)))
+                            {
+                                // Verify the resources are placed in "resources" directory
+                                if (TextFindIndex(resPathUpdated, "resources/") > 0)
+                                {
+                                    // NOTE: Look for resources in the path of the provided .c to be added
+                                    // To be copied to <category>/resources directory, extra dirs are automatically created if required
+                                    FileCopy(TextFormat("%s/%s", GetDirectoryPath(inFileName), resPathUpdated),
+                                        TextFormat("%s/%s/%s", exBasePath, exCategory, resPathUpdated));
+                                }
+                                else LOG("WARNING: Example resource must be placed in 'resources' directory next to .c file\n");
+                            }
+                            else LOG("WARNING: Example resource can not be found in: %s\n", TextFormat("%s/%s", GetDirectoryPath(inFileName), resPathUpdated));
+
+                            RL_FREE(resPathUpdated);
+                        }
+                    }
+                    else
+                    {
+                        LOG("INFO: Example resource required: %s\n", resPaths[r]);
+
+                        if (FileExists(TextFormat("%s/%s", GetDirectoryPath(inFileName), resPaths[r])))
+                        {
+                            // Verify the resources are placed in "resources" directory
+                            if (TextFindIndex(resPaths[r], "resources/") > 0)
+                            {
+                                // NOTE: Look for resources in the path of the provided .c to be added
+                                // To be copied to <category>/resources directory, extra dirs are automatically created if required
+                                FileCopy(TextFormat("%s/%s", GetDirectoryPath(inFileName), resPaths[r]),
+                                    TextFormat("%s/%s/%s", exBasePath, exCategory, resPaths[r]));
+                            }
+                            else LOG("WARNING: Example resource must be placed in 'resources' directory next to .c file\n");
+                        }
+                        else LOG("WARNING: Example resource can not be found in: %s\n", TextFormat("%s/%s", GetDirectoryPath(inFileName), resPaths[r]));
+                    }
+                }
+            }
+
+            ClearExampleResources(resPaths);
+            // -----------------------------------------------------------------------------------------
             
             // Add example to the collection list, if not already there
             // NOTE: Required format: shapes;shapes_basic_shapes;⭐️☆☆☆;1.0;4.2;"Ray";@raysan5
@@ -618,6 +679,7 @@ static int UpdateRequiredFiles(void)
     //------------------------------------------------------------------------------------------------
 
     // Edit: raylib/examples/Makefile.Web --> Update from collection
+    // NOTE: We avoid the "others" category on web building
     //------------------------------------------------------------------------------------------------
     char *mkwText = LoadFileText(TextFormat("%s/Makefile.Web", exBasePath));
     char *mkwTextUpdated = (char *)RL_CALLOC(2*1024*1024, 1); // Updated Makefile copy, 2MB
@@ -629,7 +691,8 @@ static int UpdateRequiredFiles(void)
     memcpy(mkwTextUpdated, mkwText, mkwListStartIndex);
     mkwIndex = sprintf(mkwTextUpdated + mkwListStartIndex, "#EXAMPLES_LIST_START\n");
 
-    for (int i = 0; i < MAX_EXAMPLE_CATEGORIES; i++)
+    // NOTE: We avoid the "others" category on web building
+    for (int i = 0; i < MAX_EXAMPLE_CATEGORIES - 1; i++)
     {
         mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, TextFormat("%s = \\\n", TextToUpper(exCategories[i])));
 
@@ -1018,3 +1081,75 @@ static void SortExampleByName(rlExampleInfo *items, int count)
 {
     qsort(items, count, sizeof(rlExampleInfo), rlExampleInfoCompare);
 }
+
+// Scan resource paths in example file
+static char **ScanExampleResources(const char *filePath, int *resPathCount)
+{
+    #define MAX_RES_PATH_LEN    256
+
+    char **paths = (char **)RL_CALLOC(REXM_MAX_RESOURCE_PATHS, sizeof(char **));
+    for (int i = 0; i < REXM_MAX_RESOURCE_PATHS; i++) paths[i] = (char *)RL_CALLOC(MAX_RES_PATH_LEN, sizeof(char));
+
+    int resCounter = 0;
+    char *code = LoadFileText(filePath);
+
+    if (code != NULL)
+    {
+        // Resources extensions to check
+        const char *exts[] = { ".png", ".bmp", ".jpg", ".qoi", ".gif", ".raw", ".hdr", ".ttf", ".fnt", ".wav", ".ogg", ".mp3", ".flac", ".mod", ".qoa", ".qoa", ".obj", ".iqm", ".glb", ".m3d", ".vox", ".vs", ".fs" };
+        const int extCount = sizeof(exts)/sizeof(exts[0]);
+
+        char *ptr = code;
+        while ((ptr = strchr(ptr, '"')) != NULL)
+        {
+            char *start = ptr + 1;
+            char *end = strchr(start, '"');
+            if (!end) break;
+
+            int len = end - start;
+            if ((len > 0) && (len < MAX_RES_PATH_LEN))
+            {
+                char buffer[MAX_RES_PATH_LEN] = { 0 };
+                strncpy(buffer, start, len);
+                buffer[len] = '\0';
+
+                // Check for known extensions
+                for (int i = 0; i < extCount; i++)
+                {
+                    if (IsFileExtension(buffer, exts[i]))
+                    {
+                        // Avoid duplicates
+                        bool found = false;
+                        for (int j = 0; j < resCounter; j++)
+                        {
+                            if (TextIsEqual(paths[j], buffer)) { found = true; break; }
+                        }
+
+                        if (!found && (resCounter < REXM_MAX_RESOURCE_PATHS))
+                        {
+                            strcpy(paths[resCounter], buffer);
+                            resCounter++;
+                        }
+
+                        break;
+                    }
+                }
+            }
+
+            ptr = end + 1;
+        }
+
+        UnloadFileText(code);
+    }
+
+    *resPathCount = resCounter;
+    return paths;
+}
+
+// Clear resource paths scanned
+static void ClearExampleResources(char **resPaths)
+{
+    for (int i = 0; i < REXM_MAX_RESOURCE_PATHS; i++) RL_FREE(resPaths[i]);
+
+    RL_FREE(resPaths);
+}