Commits

a1ex  committed e9aed02

PicoC: ported script parameters from CHDK (@param, @default, @range). This also required some some small backend changes.

  • Participants
  • Parent commits c0445ca

Comments (0)

Files changed (9)

File Makefile.inc

 	library_ml.o \
 	platform_ml.o \
 	picoc.o \
+	chdk-gui_script.o \
 
 endif
 
 STDIO_OBJ += \
 	lib_a-strncpy.o \
 	lib_a-ctype_.o \
+	lib_a-strrchr.o \
 
 endif
 endif

File picoc/platform_ml.c

     char *SourceStr = PlatformReadFile(FileName);
     if (!SourceStr) return;
 
-    console_printf("%s:\n"
-                   "==============================\n", 
-                   FileName);
-    console_puts(SourceStr);
-    console_puts(  "==============================\n\n");
+    console_printf("%s:\n");
+    script_define_param_variables();
+    console_puts(  "==============================\n");
+    
     msleep(500);
     PicocParse(FileName, SourceStr, strlen(SourceStr), TRUE, FALSE, TRUE);
 

File scripts/hello.c

-/** Hello, World! */
+/*
+@title Hello, World!
+@param n Pics to take
+@range n 0 5
+*/
 
 printf("Hello from PicoC!\n");
 
 sleep(2);
 
-int n = 2;
-for (int i = 0; i < 2; i++)
+for (int i = 0; i < n; i++)
 {
     printf("Taking pic %d of %d...\n", i+1, n);
     sleep(1);

File scripts/test.c

-/** PicoC testing script */
+/*
+@title PicoC testing script
+@param a Misc small tests
+@range a 0 1
+@param b Expo settings tests
+@range b 0 1
+@param c Powersave tests
+@range c 0 1
+@param d Movie recording tests
+@range d 0 1
+@param e Picture taking tests
+@range e 0 1
+*/
 
 printf("PicoC testing...\n");
 
-/* Some basic functions */
-beep();
-sleep(2);
+if (a) // misc tests
+{
+    /* Some basic functions */
+    beep();
+    sleep(2);
 
-printf("LiveView:%d Recording:%d\n", lv, recording);
+    printf("LiveView:%d Recording:%d\n", lv, recording);
 
-/* Date/time  */
-struct tm * t = get_time();
-printf("Time: %02d:%02d:%02d\n", t->hour, t->minute, t->second);
-printf("Date: %02d/%02d/%02d\n", t->year, t->month, t->day);
+    /* Date/time  */
+    struct tm * t = get_time();
+    printf("Time: %02d:%02d:%02d\n", t->hour, t->minute, t->second);
+    printf("Date: %02d/%02d/%02d\n", t->year, t->month, t->day);
 
-/* Some math */
-float pi = 3.14;
-float twopi = 2 * pi;
-printf("2*pi = %f\n", twopi);
-printf("some float values: %f %f %f %f %f %f %f %f %f %f %f %f \n", 0.0000001, 0.001, 0.123, 10000, 200000, 300000000, 5433546.45262432, 5.450267432, 0, 42, 555555.5555555, 1.0/0.0 );
+    /* Some math */
+    float pi = 3.14;
+    float twopi = 2 * pi;
+    printf("2*pi = %f\n", twopi);
+    printf("some float values: %f %f %f %f %f %f %f %f %f %f %f %f \n", 0.0000001, 0.001, 0.123, 10000, 200000, 300000000, 5433546.45262432, 5.450267432, 0, 42, 555555.5555555, 1.0/0.0 );
 
-/* Dumping memory */
-for (unsigned a = 0xFF010000; a < 0xFF010010; a+=4)
-    printf("%x: %x\n", a, *(int*)a);
+    /* Dumping memory */
+    for (unsigned addr = 0xFF010000; addr < 0xFF010010; addr += 4)
+        printf("%x: %x\n", addr, *(int*)addr);
 
-/* Key press test */
-sleep(2);
-console_hide();
-click(MENU);
-sleep(1);
-click(RIGHT);
-sleep(1);
-click(MENU);
-sleep(1);
-console_show();
-
-/* Exposure settings test (read/write) */
-printf("ISO %d 1/%f f/%f\n", get_iso(), 1/get_shutter(), get_aperture());
-printf("Sv%f Tv%f Av%f\n", get_sv(), get_tv(), get_av());
-printf("Raw ISO %d shutter %d aperture %d\n \n", get_rawiso(), get_rawshutter(), get_rawaperture());
-sleep(2);
-
-printf("setting ISO 200 1/2000 f/2.8...\n");
-set_iso(200);
-set_shutter(1./2000);
-set_aperture(2.8);
-printf(" => got ISO %d 1/%f f/%f\n\n", get_iso(), 1/get_shutter(), get_aperture());
-sleep(2);
-
-printf("setting ISO 400 1/128 f/11 (APEX)...\n");
-set_tv(7);
-set_sv(7);
-set_av(7);
-printf(" => got ISO %d 1/%f f/%f\n\n", get_iso(), 1/get_shutter(), get_aperture());
-sleep(2);
-
-printf("setting ISO 200 1/32 f/8 (raw units)...\n");
-set_rawiso(80);
-set_rawshutter(96);
-set_rawaperture(56);
-printf(" => got ISO %d 1/%f f/%f\n\n", get_iso(), 1/get_shutter(), get_aperture());
-sleep(2);
-
-/* AE tests */
-printf("AE: %d; flash AE: %d\n", get_ae(), get_flash_ae());
-set_ae(1);
-set_flash_ae(-2);
-
-/* Let's go into LiveView */
-if (!lv) { click(LV); sleep(1); }
-
-/* Kelvin tests */
-int kelvin_0 = get_kelvin();
-printf("White balance: %d K, %d G/M\n", get_kelvin(), get_green());
-set_green(0);
-printf("Kelvin from 2000 to 10000...\n");
-sleep(1);
-console_hide();
-sleep(2);
-for (int k = 2000; k < 10000; k += 50)
-{
-    set_kelvin(k);
-    sleep(0.01);
-}
-sleep(1);
-set_kelvin(kelvin_0);
-console_show();
-
-
-/* Powersave tests */
-printf("Display off... ");
-sleep(1);
-display_off();
-sleep(2);
-display_on();
-sleep(2);
-printf("and back on.\n");
-
-sleep(1);
-
-printf("LiveView paused... ");
-sleep(1);
-lv_pause();
-sleep(2);
-lv_resume();
-sleep(2);
-printf("and resumed.\n");
-
-/* Movie recording test */
-sleep(1);
-printf("Recording... ");
-movie_start();
-sleep(3);
-movie_end();
-printf("done.\n");
-sleep(1);
-
-/* Picture taking test */
-int n = 2;
-for (int i = 0; i < n; i++)
-{
-    printf("Taking pic %d of %d...\n", i+1, n);
+    /* Key press test */
+    sleep(2);
+    console_hide();
+    click(MENU);
     sleep(1);
-    takepic();
+    click(RIGHT);
+    sleep(1);
+    click(MENU);
+    sleep(1);
+    console_show();
 }
 
-printf("Taking bulb picture...\n");
-sleep(1);
-bulbpic(2.5);
+if (b) // expo tests
+{
+    /* Exposure settings test (read/write) */
+    printf("ISO %d 1/%f f/%f\n", get_iso(), 1/get_shutter(), get_aperture());
+    printf("Sv%f Tv%f Av%f\n", get_sv(), get_tv(), get_av());
+    printf("Raw ISO %d shutter %d aperture %d\n \n", get_rawiso(), get_rawshutter(), get_rawaperture());
+    sleep(2);
+
+    printf("setting ISO 200 1/2000 f/2.8...\n");
+    set_iso(200);
+    set_shutter(1./2000);
+    set_aperture(2.8);
+    printf(" => got ISO %d 1/%f f/%f\n\n", get_iso(), 1/get_shutter(), get_aperture());
+    sleep(2);
+
+    printf("setting ISO 400 1/128 f/11 (APEX)...\n");
+    set_tv(7);
+    set_sv(7);
+    set_av(7);
+    printf(" => got ISO %d 1/%f f/%f\n\n", get_iso(), 1/get_shutter(), get_aperture());
+    sleep(2);
+
+    printf("setting ISO 200 1/32 f/8 (raw units)...\n");
+    set_rawiso(80);
+    set_rawshutter(96);
+    set_rawaperture(56);
+    printf(" => got ISO %d 1/%f f/%f\n\n", get_iso(), 1/get_shutter(), get_aperture());
+    sleep(2);
+
+    /* AE tests */
+    printf("AE: %d; flash AE: %d\n", get_ae(), get_flash_ae());
+    set_ae(1);
+    set_flash_ae(-2);
+
+    /* Let's go into LiveView */
+    if (!lv) { click(LV); sleep(1); }
+
+    /* Kelvin tests */
+    int kelvin_0 = get_kelvin();
+    printf("White balance: %d K, %d G/M\n", get_kelvin(), get_green());
+    set_green(0);
+    printf("Kelvin from 2000 to 10000...\n");
+    sleep(1);
+    console_hide();
+    sleep(2);
+    for (int k = 2000; k < 10000; k += 50)
+    {
+        set_kelvin(k);
+        sleep(0.01);
+    }
+    sleep(1);
+    set_kelvin(kelvin_0);
+    console_show();
+}
+
+if (c) // powersave tests
+{
+    /* Let's go into LiveView */
+    if (!lv) { click(LV); sleep(1); }
+
+    /* Powersave tests */
+    printf("Display off... ");
+    sleep(1);
+    display_off();
+    sleep(2);
+    display_on();
+    sleep(2);
+    printf("and back on.\n");
+
+    sleep(1);
+
+    printf("LiveView paused... ");
+    sleep(1);
+    lv_pause();
+    sleep(2);
+    lv_resume();
+    sleep(2);
+    printf("and resumed.\n");
+}
+
+if (d) // movie test
+{
+    /* Movie recording test */
+    sleep(1);
+    printf("Recording... ");
+    movie_start();
+    sleep(3);
+    movie_end();
+    printf("done.\n");
+    sleep(1);
+}
+
+if (e) // photo tests
+{
+    /* Picture taking test */
+    int n = 2;
+    for (int i = 0; i < n; i++)
+    {
+        printf("Taking pic %d of %d...\n", i+1, n);
+        sleep(1);
+        takepic();
+    }
+
+    printf("Taking bulb picture...\n");
+    sleep(1);
+    bulbpic(2.5);
+}
 
 printf("Done :)\n");
 
         return -1;
     unsigned rc = FIO_ReadFile( file, buf, size );
     FIO_CloseFile( file );
-
-    if( rc == size )
-        return size;
-
-    DebugMsg( DM_MAGIC, 3, "%s: size=%d rc=%d", filename, size, rc );
-    return -1;
+    return rc;
 }
 
 

File src/chdk-gui_script.c

+// Ported from CHDK
+// (C) The CHDK team
+
+
+#include "dryos.h"
+#include "bmp.h"
+#include "string.h"
+
+#define SCRIPT_NUM_PARAMS           26
+
+#define MENUITEM_BOOL           1
+#define MENUITEM_INT            2
+#define MENUITEM_F_UNSIGNED     0x0010
+#define MENUITEM_F_MINMAX       0x0060
+#define MENU_MINMAX(min, max)   (((max)<<16)|(min&0xFFFF))
+#define MENU_MIN_SIGNED(arg)    ((short)(arg & 0xFFFF))
+#define MENU_MAX_SIGNED(arg)    ((short)((arg>>16) & 0xFFFF))
+#define MENU_MIN_UNSIGNED(arg)  ((unsigned short)(arg & 0xFFFF))
+#define MENU_MAX_UNSIGNED(arg)  ((unsigned short)((arg>>16) & 0xFFFF))
+
+const char *script_source_str = NULL;       // ptr to content of script
+static char cfg_name[100] = "\0";           // buffer to make cfg files name (paramsetnum, param_names)
+static char cfg_param_name[100] = "\0";     // buffer to make cfg param files name (params, state_before_tmprun)
+
+// ================ SCRIPT PARAMETERS ==========
+char script_title[25];                                      // Title of current script
+
+int conf_script_vars[SCRIPT_NUM_PARAMS];
+
+// 1. Values of script parameters are stored in conf.script_vars
+// 2. Encoding scheme is: array[VAR-'a'] = value
+
+#define MAX_PARAM_NAME_LEN  27
+
+char script_params[SCRIPT_NUM_PARAMS][MAX_PARAM_NAME_LEN+1];// Parameter title
+static short script_param_order[SCRIPT_NUM_PARAMS];         // Ordered as_in_script list of variables ( [idx] = id_of_var )
+                                                            // to display in same order in script
+static int script_range_values[SCRIPT_NUM_PARAMS];          // Min/Max values for param validation
+static short script_range_types[SCRIPT_NUM_PARAMS];         // Specifies if range values is signed (-9999-32767) or unsigned (0-65535)
+                                                            // Note: -9999 limit on negative values is due to current gui_menu code (and because menu only displays chars)
+static const char **script_named_values[SCRIPT_NUM_PARAMS]; // Array of list values for named parameters
+static int script_named_counts[SCRIPT_NUM_PARAMS];          // Count of # of entries in each script_list_values array
+static char *script_named_strings[SCRIPT_NUM_PARAMS];       // Base storage for named value string data
+static int script_loaded_params[SCRIPT_NUM_PARAMS];         // Copy of original values of parameters 
+                                                            // (detect are they changed or not)
+
+//-------------------------------------------------------------------
+
+const char* skip_whitespace(const char* p)  { while (*p==' ' || *p=='\t') p++; return p; }                                  // Skip past whitespace
+const char* skip_token(const char* p)       { while (*p && *p!='\r' && *p!='\n' && *p!=' ' && *p!='\t') p++; return p; }    // Skip past current token value
+const char* skip_toeol(const char* p)       { while (*p && *p!='\r' && *p!='\n') p++; return p; }                           // Skip to end of line
+const char* skip_eol(const char *p)         { p = skip_toeol(p); if (*p == '\r') p++; if (*p == '\n') p++; return p; }      // Skip past end of line
+
+//=======================================================
+//             PROCESSING "@ACTION" FUNCTIONS
+//=======================================================
+
+
+//-------------------------------------------------------------------
+static void process_title(const char *title)
+{
+    register const char *ptr = title;
+    register int i=0;
+
+    ptr = skip_whitespace(ptr);
+    while (i<(sizeof(script_title)-1) && ptr[i] && ptr[i]!='\r' && ptr[i]!='\n')
+    {
+        script_title[i]=ptr[i];
+        ++i;
+    }
+    script_title[i]=0;
+
+    script_setup_title(script_title);
+}
+
+//-------------------------------------------------------------------
+// Process one entry "@param VAR TITLE" to check if it exists
+//      param = ptr right after descriptor (should point to var)
+// RETURN VALUE: 0 if not found, 1..26 = id of var
+// Used to ensure that a param loaded from an old saved paramset does
+// not overwrite defaults from script
+//-------------------------------------------------------------------
+static int check_param(const char *param)
+{
+    register const char *ptr = param;
+    register int n=0, i=0, l;
+
+    ptr = skip_whitespace(ptr);
+    if (ptr[0] && (ptr[0]>='a' && ptr[0]<='a'+SCRIPT_NUM_PARAMS) && (ptr[1]==' ' || ptr[1]=='\t'))
+    {
+        n = ptr[0]-'a';                                 // VAR
+        ptr = skip_whitespace(ptr+2);                   // skip to TITLE
+        l = skip_toeol(ptr) - ptr;                      // get length of TITLE
+        if (l > MAX_PARAM_NAME_LEN)
+            l = MAX_PARAM_NAME_LEN;
+        if (l != strlen(script_params[n]))              // Check length matches existing TITLE length
+            n = 0;
+        else if (strncmp(ptr,script_params[n],l) != 0)  // Check that TITLE matches existing TITLE
+            n = 0;
+        else
+            n++;
+    }
+    return n; // n=1 if '@param a' was processed, n=2 for 'b' ... n=26 for 'z'. n=0 if failed.
+}
+
+//-------------------------------------------------------------------
+// Process one entry "@param VAR TITLE"
+//      param = ptr right after descriptor (should point to var)
+// RESULT: script_params[VAR] - parameter title
+// RETURN VALUE: 0 if err, 1..26 = id of var
+//-------------------------------------------------------------------
+static int process_param(const char *param)
+{
+    register const char *ptr = param;
+    register int n=0, i=0, l;
+
+    ptr = skip_whitespace(ptr);
+    if (ptr[0] && (ptr[0]>='a' && ptr[0]<='a'+SCRIPT_NUM_PARAMS) && (ptr[1]==' ' || ptr[1]=='\t'))
+    {
+        n = ptr[0]-'a';
+        ptr = skip_whitespace(ptr+2);
+        l = skip_toeol(ptr) - ptr;                  // get length of TITLE
+        if (l > MAX_PARAM_NAME_LEN)
+            l = MAX_PARAM_NAME_LEN;
+        strncpy(script_params[n],ptr,l);
+        n++;
+    }
+    return n; // n=1 if '@param a' was processed, n=2 for 'b' ... n=26 for 'z'. n=0 if failed.
+}
+
+//-------------------------------------------------------------------
+// Process one entry "@default VAR VALUE"
+//      param = ptr right after descriptor (should point to var)
+//-------------------------------------------------------------------
+static void process_default(const char *param)
+{
+    register const char *ptr = param;
+    register int n;
+
+    ptr = skip_whitespace(ptr);
+    if (ptr[0] && (ptr[0]>='a' && ptr[0]<='a'+SCRIPT_NUM_PARAMS) && (ptr[1]==' ' || ptr[1]=='\t'))
+    {
+        n = ptr[0]-'a';
+        ptr = skip_whitespace(ptr+2);
+        conf_script_vars[n] = strtol(ptr, NULL, 0);
+        script_loaded_params[n] = conf_script_vars[n];
+    } // ??? else produce error message
+}
+
+//-------------------------------------------------------------------
+// Process one entry "@range VAR MIN MAX"
+//      param = ptr right after descriptor (should point to var)
+//-------------------------------------------------------------------
+static void process_range(const char *param)
+{
+    register const char *ptr = param;
+    register int n;
+
+    ptr = skip_whitespace(ptr);
+    if (ptr[0] && (ptr[0]>='a' && ptr[0]<='a'+SCRIPT_NUM_PARAMS) && (ptr[1]==' ' || ptr[1]=='\t'))
+    {
+        n = ptr[0]-'a';
+        ptr = skip_whitespace(ptr+2);
+        int min = strtol(ptr,NULL,0);
+        ptr = skip_whitespace(skip_token(ptr));
+        int max = strtol(ptr,NULL,0);
+        script_range_values[n] = MENU_MINMAX(min,max);
+        if ((min == 0) && (max == 1))
+            script_range_types[n] = MENUITEM_BOOL;
+        else if ((min >= 0) && (max >= 0)) 
+            script_range_types[n] = MENUITEM_INT|MENUITEM_F_MINMAX|MENUITEM_F_UNSIGNED;
+        else
+            script_range_types[n] = MENUITEM_INT|MENUITEM_F_MINMAX;
+    } // ??? else produce error message
+}
+
+#if 0 // not yet working
+//-------------------------------------------------------------------
+// Process one entry "@values VAR A B C D ..."
+//      param = ptr right after descriptor (should point to var)
+//-------------------------------------------------------------------
+static void process_values(const char *param)
+{
+    register const char *ptr = param;
+    register int n;
+
+    ptr = skip_whitespace(ptr);
+    if (ptr[0] && (ptr[0]>='a' && ptr[0]<='a'+SCRIPT_NUM_PARAMS) && (ptr[1]==' ' || ptr[1]=='\t'))
+    {
+        n = ptr[0]-'a';
+        ptr = skip_whitespace(ptr+2);
+        int len = skip_toeol(ptr) - ptr;
+        script_named_strings[n] = malloc(len+1);
+        strncpy(script_named_strings[n], ptr, len);
+        script_named_strings[n][len] = 0;
+
+        const char *p = script_named_strings[n];
+        int cnt = 0;
+        while (*p)
+        {
+            p = skip_whitespace(skip_token(p));
+            cnt++;
+        }
+        script_named_counts[n] = cnt;
+        script_named_values[n] = malloc(cnt * sizeof(char*));
+
+        p = script_named_strings[n];
+        cnt = 0;
+        while (*p)
+        {
+            script_named_values[n][cnt] = p;
+            p = skip_token(p);
+            if (*p)
+            {
+                *((char*)p) = 0;
+                p = skip_whitespace(p+1);
+            }
+            cnt++;
+        }
+    } // ??? else produce error message
+}
+#endif
+
+//=======================================================
+//                 SCRIPT LOADING FUNCTIONS
+//=======================================================
+
+//-------------------------------------------------------------------
+// PURPOSE: Parse script (script_source_str) for @xxx
+// PARAMETERS:  fn - full path of script
+// RESULTS:  script_title
+//           script_params
+//           script_params_order
+//           script_loaded_params, conf.script_vars
+//-------------------------------------------------------------------
+void script_scan(const char *fn, const char * script_source_str)
+{
+    register const char *ptr = script_source_str;
+    register int i, j=0, n;
+    char *c;
+
+    // Build title
+
+    c=strrchr(fn, '/');
+    strncpy(script_title, (c)?c+1:fn, sizeof(script_title));
+    script_title[sizeof(script_title)-1]=0;
+
+    // Reset everything
+
+    for (i=0; i<SCRIPT_NUM_PARAMS; ++i)
+    {
+        conf_script_vars[i] = 0;
+        script_loaded_params[i] = 0;
+        script_params[i][0]=0;
+        script_param_order[i]=0;
+        script_range_values[i] = 0;
+        if (script_named_values[i]) free(script_named_values[i]);
+        script_named_values[i] = 0;
+        if (script_named_strings[i]) free(script_named_strings[i]);
+        script_named_strings[i] = 0;
+        script_named_counts[i] = 0;
+        script_range_types[i] = 0;
+    }
+
+    // Fillup order, defaults
+
+    while (ptr[0])
+    {
+        ptr = skip_whitespace(ptr);
+        if (ptr[0]=='@') {
+            if (strncmp("@title", ptr, 6)==0)
+            {
+                process_title(ptr+6);
+            }
+            else if (strncmp("@param", ptr, 6)==0)
+            {
+                n = process_param(ptr+6); // n=1 if '@param a' was processed, n=2 for 'b' ... n=26 for 'z'. n=0 if failed.
+                if (n>0 && n<=SCRIPT_NUM_PARAMS)
+                {
+                  script_param_order[j]=n;
+                  j++;
+                }
+            }
+            else if (strncmp("@default", ptr, 8)==0)
+            {
+                process_default(ptr+8);
+            }
+            else if (strncmp("@range", ptr, 6)==0)
+            {
+                process_range(ptr+6);
+            }
+            #if 0
+            else if (strncmp("@values", ptr, 7)==0)
+            {
+                process_values(ptr+7);
+            }
+            #endif
+        }
+        ptr = skip_eol(ptr);
+    }
+}
+
+
+void script_update_menu()
+{
+    int j = 0;
+    for (int i = 0; i < SCRIPT_NUM_PARAMS; i++)
+    {
+        int p = script_param_order[i];
+        if (!p) continue;
+        p--;
+        int min = MENU_MIN_SIGNED(script_range_values[p]);
+        int max = MENU_MAX_SIGNED(script_range_values[p]);
+        if (script_range_types[p] == 0) // no min/max, use some default range
+        {
+            min = -1000;
+            max = 1000;
+        }
+        
+        // if our name is short, fill it with spaces
+        int n = strlen(script_params[p]);
+        int k;
+        for (k = n; k < MAX_PARAM_NAME_LEN-2; k++)
+            script_params[p][k] = ' ';
+        script_params[p][k] = 0;
+
+        script_setup_param(j++, script_params[p], &conf_script_vars[p], min, max);
+    }
+}
+
+#include "picoc.h"
+
+// call this right before running the script
+void script_define_param_variables()
+{
+    int j = 0;
+    for (int i = 0; i < SCRIPT_NUM_PARAMS; i++)
+    {
+        int p = script_param_order[i];
+        if (!p) continue;
+        p--;
+        int* v = &conf_script_vars[p];
+        int _varname = 'a' + p;
+        char* varname = &_varname;
+        extern struct ValueType IntType;
+        VariableDefinePlatformVar(NULL, varname, &IntType, (union AnyValue *)&conf_script_vars[p], FALSE);
+        console_printf("   Param %s = %d; // %s\n", varname, conf_script_vars[p], script_params[p]);
+    }
+}
             struct menu_entry * child = entry->children;
             while (!MENU_IS_EOL(child)) { count++; child++; }
             struct menu * submenu = menu_find_by_name( entry->name, ICON_ML_SUBMENU);
-            menu_add(entry->name, entry->children, count);
+            // only one submenu can have a given name
+            // if more submenus have the same name, the submenu is reused, don't create it again
+            if (!submenu->children)
+                menu_add(entry->name, entry->children, count);
             submenu->submenu_width = entry->submenu_width;
             submenu->submenu_height = entry->submenu_height;
         }
     if (icon_drawn) return;
     icon_drawn = type;
     x -= 40;
-    if (type != MNI_NONE) bmp_printf(FONT_LARGE, x, y, "  "); // cleanup background; don't call this for LCD remote icons
+    if (type != MNI_NONE && type != MNI_STOP_DRAWING) 
+        bmp_printf(FONT_LARGE, x, y, "  "); // cleanup background; don't call this for LCD remote icons
     warning_msg = 0;
     switch(type)
     {
                     dim_hidden_menu(x, y, menu->selected);
             }
             
+            // menu->display asked to draw the entire screen by itself? stop drawing right now
+            if (icon_drawn == MNI_STOP_DRAWING)
+                return;
+            
             // this should be after menu->display, in order to allow it to override the icon
             if (menu->selected || (!menu_lv_transparent_mode && !only_selected))
             {
         struct menu_entry * entry = menu->children;
         while( entry )
         {
-            if (!IS_VISIBLE(entry))
+            if (!IS_VISIBLE(entry) && entry->name)
             {
                 if (hidden_count) { STR_APPEND(hidden_msg, ", "); }
                 int len = strlen(hidden_msg);
 #define MNI_BOOL(x) ((x) ? MNI_ON : MNI_OFF)
 #define MNI_BOOL_AUTO(x) ((x) == 1 ? MNI_ON : (x) == 0 ? MNI_OFF : MNI_AUTO)
 
+#define MNI_STOP_DRAWING -100
+
 #define _ZEBRAS_IN_LIVEVIEW (get_global_draw_setting() & 1)
 #define GDR_WARNING_MSG ((lv && lv_disp_mode && _ZEBRAS_IN_LIVEVIEW) ? "Press " INFO_BTN_NAME " (outside ML menu) to turn Canon displays off." : get_global_draw_setting() ? "GlobalDraw is disabled, check your settings." : "GlobalDraw is OFF.")
 
 #define MAX_SCRIPT_NUM 11
 #define FILENAME_SIZE 15
 #define SCRIPT_TITLE_SIZE 25
-#define PICOC_HEAP_SIZE (30*1024)
+#define PICOC_HEAP_SIZE (128*1024)
 
 static char script_list[MAX_SCRIPT_NUM][FILENAME_SIZE];
 static char script_titles[MAX_SCRIPT_NUM][SCRIPT_TITLE_SIZE];
     return path;
 }
 
-static void guess_script_title_from_first_line(char* filename, char* output, int maxsize)
+static void guess_script_title_from_first_line(char* src, char* dst, int maxsize)
 {
-    char* dst = output;
-    
-    char buf[32];
-    int r = read_file(filename, buf, sizeof(buf));
-    if (r <= 0)
-    {
-        snprintf(dst, maxsize, "Error");
-        return;
-    }
-    
-    char* src = buf;
     while (isspace(*src) || *src == '/' || *src == '*') // skip comments
         src++;
 
 }
 static void script_parse_header(int script_index)
 {
-    guess_script_title_from_first_line(get_script_path(script_index), script_titles[script_index], SCRIPT_TITLE_SIZE);
+    script_selected = script_index; // side effect (!)
+
+    // clear params submenu (hide all unused stuff)
+    script_reset_params();
+
+    // try to read the script 
+    static char _buf[1025];
+    char* buf = UNCACHEABLE(_buf);
+    char* fn = get_script_path(script_selected);
+    int r = read_file(fn, buf, sizeof(_buf)-1);
+    if (r < 0)
+    {
+        snprintf(script_titles[script_index], SCRIPT_TITLE_SIZE, "Error");
+        return;
+    }
+    buf[r+1] = 0;
+    
+    // get some default title
+    guess_script_title_from_first_line(buf, script_titles[script_index], SCRIPT_TITLE_SIZE);
+    
+    // parse CHDK script header
+    script_scan(fn, buf);
+    
+    // update submenu
+    script_update_menu();
 }
 
 /* modify from is_valid_cropmark_filename */
 static void
 script_run_display( void * priv, int x, int y, int selected )
 {
-    if (script_preview_flag)
-    {
-        menu_draw_icon(x, y, -1, 0);
-        return;
-    }
-
     bmp_printf(
         selected ? MENU_FONT_SEL : MENU_FONT,
         x, y,
     bmp_fill(40, 0, 0, 720, 430);
     int fnt = FONT(FONT_MED, COLOR_WHITE, 40);
     big_bmp_printf(fnt, 10, 10, "%s", script_preview);
-    menu_draw_icon(x, y, -1, 0);
+    menu_draw_icon(x, y, MNI_STOP_DRAWING, 0);
 }
 
 /*static void script_select(void* priv, int delta)
     return 1;
 }
 
-extern void menu_open_submenu();
+void script_open_submenu()
+{
+    // update the submenu structure on the fly
+    static int prev_selected = -1;
+    if (prev_selected != script_selected)
+    {
+        script_parse_header(script_selected);
+    }
+    prev_selected = script_selected;
+    
+    // now we can display it :)
+    menu_open_submenu();
+}
 
 static void
 script_display( void * priv, int x, int y, int selected )
         "%s",
         script_titles[script_displayed]
     );
-    
-    /*
-    if (selected)
-    {
-        // display the first 59 characters from the script as help text
-        static char help[59];
-        static int prev_selected = -1;
-        if (prev_selected != script_selected)
-            read_file(get_script_path(script_selected), help, sizeof(help));
-        prev_selected = script_selected;
-        for (char* c = help; *c; c++)
-            if (*c == '\n') { *c = ' '; }
-        bmp_printf(FONT_MED, 10, 453, "%s", help);
-    }
-    */
-
     menu_draw_icon(x, y, MNI_SUBMENU, selected);
 }
 
+static struct menu_entry picoc_submenu[] = {
+        {
+            .name = "Show script",
+            .priv = &script_preview_flag,
+            .max = 1,
+            .icon_type = IT_ACTION,
+            .display    = script_print,
+            .help = "Display the contents of the selected script.",
+        },
+        {
+            .name = "Run script",
+            .display    = script_run_display,
+            .select        = script_run_fun,
+            .help = "Execute the selected script.",
+        },
+        {
+            .help = "Script parameter #1",
+        },
+        {
+            .help = "Script parameter #2",
+        },
+        {
+            .help = "Script parameter #3",
+        },
+        {
+            .help = "Script parameter #4",
+        },
+        {
+            .help = "Script parameter #5",
+        },
+        {
+            .help = "Script parameter #6",
+        },
+        {
+            .help = "Script parameter #7",
+        },
+        MENU_EOL
+};
+
+#define MAX_PARAMS (COUNT(picoc_submenu) - 3)
+
 static struct menu_entry picoc_menu[] = {
     /*
     {
 
 #define SCRIPT_ENTRY(i) \
         { \
-            .name = script_list[i], \
+            .name = "Selected script", \
             .priv = (void*)i, \
-            .select = menu_open_submenu, \
+            .select = script_open_submenu, \
+            .select_Q = script_open_submenu, \
             .display = script_display, \
-            .children =  (struct menu_entry[]) { \
-                { \
-                    .name = "Show script", \
-                    .priv = &script_preview_flag, \
-                    .max = 1, \
-                    .icon_type = IT_ACTION, \
-                    .display    = script_print, \
-                    .help = "Display the contents of the selected script.", \
-                }, \
-                { \
-                    .name = "Run script", \
-                    .display    = script_run_display, \
-                    .select        = script_run_fun, \
-                    .help = "Execute the selected script.", \
-                }, \
-                MENU_EOL, \
-            }, \
+            .submenu_width = 700, \
+            .children = picoc_submenu, \
+            .help = "Run small C-like scripts. http://code.google.com/p/picoc/", \
         },
     
     SCRIPT_ENTRY(0)
     SCRIPT_ENTRY(10)
 };
 
+void script_setup_param(
+    int param_index,  // 0-5
+    char* param_name, // e.g. "Number of shots"
+    int* param_value, // pointer to param value (priv)
+    int min_value, 
+    int max_value
+    )
+{
+    if (param_index >= MAX_PARAMS) return;
+    struct menu_entry * entry = &(picoc_menu[script_selected].children[2 + param_index]);
+    entry->name = param_name;
+    entry->priv = param_value;
+    entry->min = min_value;
+    entry->max = max_value;
+    entry->hidden = 0;
+}
+
+void script_reset_params()
+{
+    for (int i = 0; i < MAX_PARAMS; i++)
+    {
+        struct menu_entry * entry = &(picoc_menu[script_selected].children[2 + i]);
+        entry->name = 0;
+        entry->priv = 0;
+        entry->min = 0;
+        entry->max = 0;
+        entry->icon_type = 0;
+        entry->hidden = 1;
+    }
+}
+
+void script_setup_title(char* script_title)
+{
+    snprintf(script_titles[script_selected], SCRIPT_TITLE_SIZE, "%s", script_title);
+}
+
 static void picoc_init()
 {
     find_scripts();