1414// CONFIG_SPI_FLASH_DANGEROUS_WRITE_ALLOWED 
1515
1616#define  MAX_PARTITIONS  (24) // ESP_PARTITION_TABLE_MAX_ENTRIES
17+ #define  ALIGN_BLOCK (val , alignment ) ((int)(((val) + (alignment - 1)) / alignment) * alignment)
1718
1819#define  RETRO_GO_IMG_MAGIC  "RG_IMG_0"
1920typedef  struct 
@@ -27,155 +28,204 @@ typedef struct
2728    char  reserved [156 ];
2829} img_info_t ;
2930
30- #define  FORMAT (message ...) ({snprintf(message_buffer, sizeof(message_buffer), message); message_buffer; })
31- #define  CONFIRM (title , message ...)                             \
32-     {                                                          \
33-         if (!rg_gui_confirm(_(title), FORMAT(message), false)) \
34-             return false;                                      \
35-         rg_display_clear(C_BLACK);                             \
36-     }
37- #define  TRY (cond , error_message )                 \
38-     if (!(cond))                                 \
39-     {                                            \
40-         rg_gui_alert(_("Error"), error_message); \
41-         return false;                            \
42-     }
43- 
4431typedef  struct 
4532{
46-     const  esp_partition_info_t  * src ;
47-     const  esp_partition_t  * dst ;
48- } partition_pair_t ;
33+     char  name [17 ];
34+     struct  {int  offset , size ;} src ;
35+     struct  {int  offset , size ;} dst ;
36+ } flash_task_t ;
4937
38+ static  size_t  gp_buffer_size  =  0x20000 ;
39+ static  void  * gp_buffer  =  NULL ;
5040static  rg_app_t  * app ;
51- // static esp_partition_info_t device_partition_table[ESP_PARTITION_TABLE_MAX_ENTRIES]; 
5241
53- static  bool  do_update (const  char  * filename )
42+ #define  FORMAT (message ...) ({snprintf(message_buffer, sizeof(message_buffer), message); message_buffer; })
43+ #define  TRY (cond , error_message ...)                      \
44+     RG_LOGD("Line: %s", #cond);                          \
45+     if (!(cond))                                         \
46+     {                                                    \
47+         rg_gui_alert(_("Error"), FORMAT(error_message)); \
48+         goto fail;                                       \
49+     }
50+ 
51+ static  bool  fread_at (void  * output , int  offset , int  length , FILE  * fp )
52+ {
53+     if  (offset  <  0  &&  !(fseek (fp , 0 , SEEK_END ) ==  0  &&  fseek (fp , offset , SEEK_CUR ) ==  0 ))
54+         return  false;
55+     else  if  (offset  >= 0  &&  !(fseek (fp , offset , SEEK_SET ) ==  0 ))
56+         return  false;
57+     return  fread (output , length , 1 , fp ) ==  1 ;
58+ }
59+ 
60+ static  bool  parse_file (esp_partition_info_t  * partition_table , size_t  * num_partitions , FILE  * fp )
5461{
5562    char  message_buffer [256 ];
56-     esp_partition_info_t  partition_table [16 ]; // ESP_PARTITION_TABLE_MAX_ENTRIES 
57-     int  num_partitions  =  0 ;
5863    img_info_t  img_info ;
59-     void  * buffer ;
60-     int  filesize  =  0 ;
61-     FILE  * fp ;
62- 
63-     RG_LOGI ("Filename: %s" , filename );
64-     rg_display_clear (C_BLACK );
65- 
66-     TRY (fp  =  fopen (filename , "rb" ), "File open failed" );
67-     TRY (fseek (fp , 0 , SEEK_END ) ==  0 , "File seek failed" );
68-     TRY (fseek (fp , - sizeof (img_info_t ), SEEK_CUR ) ==  0 , "File seek failed" );
69-     filesize  =  ftell (fp ); // Size without the footer 
70-     TRY (fread (& img_info , sizeof (img_info ), 1 , fp ), "File read failed" );
64+     int  _num_partitions ;
7165
72-     img_info .name [sizeof (img_info .name ) -  1 ] =  0 ;   // Just in case 
66+     TRY (fread_at (& img_info , - sizeof (img_info_t ), sizeof (img_info_t ), fp ), "File read failed" );
67+     img_info .name [sizeof (img_info .name ) -  1 ] =  0 ;       // Just in case 
7368    img_info .version [sizeof (img_info .version ) -  1 ] =  0 ; // Just in case 
7469    img_info .target [sizeof (img_info .target ) -  1 ] =  0 ;   // Just in case 
7570
76-     if  (memcmp (img_info .magic , RETRO_GO_IMG_MAGIC , 8 ) !=  0 ) // Invalid  image 
71+     if  (memcmp (img_info .magic , RETRO_GO_IMG_MAGIC , 8 ) ==  0 ) // Valid retro-go  image 
7772    {
78-         CONFIRM ("Warning" , "File is not a valid Retro-Go image.\nContinue anyway?" );
73+         if  (strcasecmp (img_info .target , RG_TARGET_NAME ) !=  0 ) // Wrong target 
74+         {
75+             FORMAT ("The file appears to be for a different device.\n" 
76+                    "Current device: %s\n" 
77+                    "Image device: %s\n\n" 
78+                    "Do you want to continue anyway?" ,
79+                    RG_TARGET_NAME ,
80+                    img_info .target );
81+             if  (!rg_gui_confirm ("Warning" , message_buffer , false))
82+                 goto fail ;
83+         }
84+         // TODO: Validate checksum 
85+         FORMAT ("Current version: %s\nNew version: %s\n\nContinue?" , app -> version , img_info .version );
86+         if  (!rg_gui_confirm ("Warning" , message_buffer , false))
87+             goto fail ;
7988    }
80-     else  if  (strcasecmp ( img_info . target ,  RG_TARGET_NAME )  !=   0 )  // Wrong target 
89+     else  if  (! rg_gui_confirm ( "Warning" ,  "File is not a valid Retro-Go image.\nContinue anyway?" , false)) 
8190    {
82-         CONFIRM ("Warning" ,
83-                 "The file appears to be for a different device.\n" 
84-                 "Current device: %s\n" 
85-                 "Image device: %s\n\n" 
86-                 "Do you want to continue anyway?" ,
87-                 RG_TARGET_NAME ,
88-                 img_info .target );
91+         goto fail ;
92+     }
93+     // TODO: Also support images that truncate the first 0x1000, just in case 
94+     TRY (fread_at (gp_buffer , ESP_PARTITION_TABLE_OFFSET , ESP_PARTITION_TABLE_MAX_LEN , fp ), "File read failed" );
95+     TRY (esp_partition_table_verify ((const  esp_partition_info_t  * )gp_buffer , true, & _num_partitions ) ==  ESP_OK , "File is not a valid ESP32 image." );
96+     memcpy (partition_table , gp_buffer , sizeof (esp_partition_info_t ) *  _num_partitions );
97+     * num_partitions  =  _num_partitions ;
98+     return  true;
99+ fail :
100+     return  false;
101+ }
102+ 
103+ static  bool  do_flash (flash_task_t  * queue , size_t  queue_count , FILE  * fp )
104+ {
105+     char  message_buffer [256 ];
106+     rg_gui_option_t  lines [queue_count  +  4 ];
107+     // size_t lines_count = 0; 
108+ 
109+     for  (size_t  i  =  0 ; i  <  queue_count ; ++ i )
110+         lines [i ] =  (rg_gui_option_t ){0 , queue [i ].name , NULL , RG_DIALOG_FLAG_NORMAL , NULL };
111+     lines [queue_count  +  0 ] =  (rg_gui_option_t )RG_DIALOG_SEPARATOR ;
112+     lines [queue_count  +  1 ] =  (rg_gui_option_t ){5 , "Proceed!" , NULL , RG_DIALOG_FLAG_NORMAL , NULL };
113+     lines [queue_count  +  2 ] =  (rg_gui_option_t )RG_DIALOG_END ;
114+ 
115+     if  (rg_gui_dialog ("Partitions to be updated:" , lines , -1 ) !=  5 )
116+         goto fail ;
117+ 
118+ #define  TRY_F (msg , cond , err )                                  \
119+     RG_LOGD("Line: %s", #cond);                                \
120+     rg_display_clear(C_BLACK);                                 \
121+     lines[i].flags = RG_DIALOG_FLAG_NORMAL;                    \
122+     lines[i].value = msg;                                      \
123+     rg_gui_draw_dialog("Progress", lines, queue_count, i);     \
124+     if (!(cond))                                               \
125+     {                                                          \
126+         lines[i].value = err;                                  \
127+         rg_gui_draw_dialog("Progress", lines, queue_count, i); \
128+         errors++;                                              \
129+         break;                                                 \
89130    }
90-     else  // Valid image 
131+ 
132+     int  errors  =  0 ;
133+ 
134+     for  (size_t  i  =  0 ; i  <  queue_count ; ++ i )
135+         lines [i ].value  =  "Pending" , lines [i ].flags  =  RG_DIALOG_FLAG_DISABLED ;
136+ 
137+     for  (size_t  i  =  0 ; i  <  queue_count ; ++ i )
91138    {
92-         CONFIRM ("Version" , "Current version: %s\nNew version: %s\n\nContinue?" , app -> version , img_info .version );
139+         const  flash_task_t  * t  =  & queue [i ];
140+         TRY_F ("Erasing" , spi_flash_erase_range (t -> dst .offset , t -> dst .size ) ==  ESP_OK  ||  true, "Erase err" );
141+         int  offset  =  0 , size  =  t -> src .size ;
142+         while  (size  >  0 )
143+         {
144+             int  chunk_size  =  ALIGN_BLOCK (RG_MIN (size , gp_buffer_size ), 0x1000 );
145+             TRY_F ("Reading" , fread_at (gp_buffer , t -> src .offset  +  offset , chunk_size , fp ), "Read err" );
146+             TRY_F ("Writing" , spi_flash_write (t -> dst .offset  +  offset , gp_buffer , chunk_size ) ==  ESP_OK , "Write err" );
147+             offset  +=  chunk_size ;
148+             size  -=  chunk_size ;
149+         }
150+         TRY_F ("Complete" , size  ==  0 , "Failed" );
93151    }
94152
95-     // TODO: Also support images that truncate the first 0x1000, just in case 
96-     TRY (fseek (fp , ESP_PARTITION_TABLE_OFFSET , SEEK_SET ) ==  0 , "File seek failed" );
97-     TRY (fread (& partition_table , sizeof (partition_table ), 1 , fp ), "File read failed" );
98-     TRY (esp_partition_table_verify (partition_table , true, & num_partitions ) ==  ESP_OK , "File is not a valid ESP32 image.\nCannot continue." );
153+     lines [queue_count  +  0 ] =  (rg_gui_option_t ){0 , FORMAT ("  - %d errors -  " , errors ), NULL , RG_DIALOG_FLAG_NORMAL , NULL };
154+     lines [queue_count  +  1 ] =  (rg_gui_option_t ){0 , "Complete!" , NULL , RG_DIALOG_FLAG_NORMAL , NULL };
155+     lines [queue_count  +  2 ] =  (rg_gui_option_t )RG_DIALOG_END ;
156+     rg_gui_dialog ("Complete" , lines , -1 );
157+     return  true;
158+ fail :
159+     return  false;
160+ }
99161
100-     const  char  * current_partition  =  esp_ota_get_running_partition ()-> label ;
101-     partition_pair_t  queue [num_partitions ];
162+ static  bool  do_update_dangerous (const  char  * filename )
163+ {
164+     return  false;
165+ }
166+ 
167+ static  bool  do_update (const  char  * filename )
168+ {
169+     esp_partition_info_t  partition_table [MAX_PARTITIONS ] =  {0 };
170+     size_t  num_partitions  =  0 ;
171+     flash_task_t  queue [MAX_PARTITIONS ] =  {0 };
102172    size_t  queue_count  =  0 ;
173+     char  message_buffer [256 ];
174+     FILE  * fp  =  NULL ;
103175
176+     RG_LOGI ("Filename: %s" , filename );
177+     rg_display_clear (C_BLACK );
178+ 
179+     TRY (fp  =  fopen (filename , "rb" ), "File open failed" );
180+     if  (!parse_file (partition_table , & num_partitions , fp ))
181+         goto fail ;
182+ 
183+     const  char  * current_partition  =  esp_ota_get_running_partition ()-> label ;
104184    // At this time we only flash partitions of type app and subtype ota_X 
105-     for  (int  i  =  0 ; i  <  num_partitions ; ++ i )
185+     for  (size_t  i  =  0 ; i  <  num_partitions ; ++ i )
106186    {
107187        const  esp_partition_info_t  * src  =  & partition_table [i ];
108188        const  esp_partition_t  * dst  =  esp_partition_find_first (src -> type , ESP_PARTITION_SUBTYPE_ANY , (char  * )src -> label );
109189
110190        if  (src -> type  !=  PART_TYPE_APP  ||  (src -> subtype  &  0xF0 ) !=  PART_SUBTYPE_OTA_FLAG )
111-             RG_LOGW ("Skipping partition %.15s : Unsupported type." , (char  * )src -> label );
191+             RG_LOGW ("Skipping partition %.16s : Unsupported type." , (char  * )src -> label );
112192        else  if  (!dst )
113-             RG_LOGW ("Skipping partition %.15s : No match found." , (char  * )src -> label );
193+             RG_LOGW ("Skipping partition %.16s : No match found." , (char  * )src -> label );
114194        else  if  ((dst -> subtype  &  0xF0 ) !=  PART_SUBTYPE_OTA_FLAG )
115-             RG_LOGW ("Skipping partition %.15s : Not an OTA partition." , (char  * )src -> label );
195+             RG_LOGW ("Skipping partition %.16s : Not an OTA partition." , (char  * )src -> label );
116196        else  if  (strncmp (current_partition , dst -> label , 16 ) ==  0 )
117-             RG_LOGW ("Skipping partition %.15s : Currently running." , (char  * )src -> label );
197+             RG_LOGW ("Skipping partition %.16s : Currently running." , (char  * )src -> label );
118198        else  if  (dst -> size  <  src -> pos .size )
119-             RG_LOGW ("Skipping partition %.15s : New partition is bigger." , (char  * )src -> label );
199+             RG_LOGW ("Skipping partition %.16s : New partition is bigger." , (char  * )src -> label );
120200        else 
121201        {
122-             queue [queue_count ].src  =  src ;
123-             queue [queue_count ].dst  =  dst ;
124-             queue_count ++ ;
202+             RG_LOGI ("Partition %.16s can be updated!" , (char  * )src -> label );
203+             flash_task_t  * task  =  memset (& queue [queue_count ++ ], 0 , sizeof (flash_task_t ));
204+             memcpy (task -> name , src -> label , 16 );
205+             task -> src .offset  =  src -> pos .offset ;
206+             task -> src .size  =  src -> pos .size ;
207+             task -> dst .offset  =  dst -> address ;
208+             task -> dst .size  =  dst -> size ;
125209        }
126210    }
127211
128-     TRY (queue_count  >  0 , "Found no updatable partition!" );
129- 
130-     int  pos  =  0 ;
131-     for  (size_t  i  =  0 ; i  <  queue_count ; ++ i )
132-         pos  +=  snprintf (message_buffer  +  pos , sizeof (message_buffer ) -  pos , "- %s\n" , queue [i ].dst -> label );
133-     pos  +=  snprintf (message_buffer  +  pos , sizeof (message_buffer ) -  pos , "\nProceed?" );
134- 
135-     if  (!rg_gui_confirm ("Partitions to be updated:" , message_buffer , false))
136-         return  false;
137- 
138-     TRY (buffer  =  malloc (2  *  1024  *  1024 ), "Memory allocation failed" );
139- 
140-     // TODO: Implement scrolling. Maybe on rg_gui side? 
141-     //       Or at least support continue writing on the same line. "... Complete" 
142-     int  y_pos  =  9999 ;
143-     #define  SCREEN_PRINTF (color , message ...)                                               \
144-         if (y_pos + 16 > rg_display_get_height())                                          \
145-             rg_display_clear(C_BLACK), y_pos = 0;                                          \
146-         y_pos += rg_gui_draw_text(0, y_pos, 0, FORMAT(message), color, C_BLACK, 0).height;
147- 
148-     SCREEN_PRINTF (C_WHITE , "Starting..." );
212+     rg_display_clear (C_BLACK );
149213
150-     for  ( size_t   i   =   0 ;  i   <   queue_count ;  ++ i )
214+     if  ( queue_count   ==   0 )
151215    {
152-         const  esp_partition_info_t  * src  =  queue [i ].src ;
153-         const  esp_partition_t  * dst  =  queue [i ].dst ;
154-         esp_ota_handle_t  ota_handle ;
155-         esp_err_t  ret ;
156-         if  ((ret  =  esp_ota_begin (queue [i ].dst , 0 , & ota_handle )) !=  ESP_OK )
157-         {
158-             SCREEN_PRINTF (C_RED , "Skipping %s: %s" , dst -> label , esp_err_to_name (ret ));
159-             continue ;
160-         }
161-         SCREEN_PRINTF (C_WHITE , "Reading %s from file..." , dst -> label );
162-         if  (fseek (fp , src -> pos .offset , SEEK_SET ) !=  0  ||  !fread (buffer , src -> pos .size , 1 , fp ))
163-         {
164-             SCREEN_PRINTF (C_RED , "File read error" );
165-             continue ;
166-         }
167-         SCREEN_PRINTF (C_WHITE , "Writing %s to flash..." , dst -> label );
168-         if  ((ret  =  esp_ota_write (ota_handle , buffer , src -> pos .size )) ==  ESP_OK ) {
169-             SCREEN_PRINTF (C_GREEN , "Complete!" );
170-         } else  {
171-             SCREEN_PRINTF (C_RED , "Failed: %s" , esp_err_to_name (ret ));
172-         }
173-         esp_ota_end (ota_handle );
216+         // Try dangerous update 
217+         // goto fail; 
174218    }
175-     free (buffer );
176219
177-     rg_gui_alert (_ ("All done" ), "The process is complete" );
220+     if  (!do_flash (queue , queue_count , fp ))
221+         goto fail ;
222+ 
223+     fclose (fp );
178224    return  true;
225+ fail :
226+     if  (fp )
227+         fclose (fp );
228+     return  false;
179229}
180230
181231void  app_main (void )
@@ -187,22 +237,22 @@ void app_main(void)
187237        .isLauncher  =  true,
188238    });
189239
190-     // if (spi_flash_read(ESP_PARTITION_TABLE_OFFSET, &device_partition_table, sizeof(device_partition_table)) != ESP_OK) 
191-     // { 
192-     //     rg_gui_alert(_("Error"), "Failed to read device's partition table!"); 
193-     //     rg_system_exit(); 
194-     // } 
240+     gp_buffer  =  rg_alloc (gp_buffer_size , MEM_FAST );
241+     if  (!gp_buffer )
242+         RG_PANIC ("Memory allocation failed" );
195243
196244    // const char *filename = app->romPath; 
197245    while  (true)
198246    {
199247        char  * filename  =  rg_gui_file_picker ("Select update" , RG_BASE_PATH_UPDATES , NULL , true, true);
248+         rg_display_clear (C_BLACK );
200249        if  (!filename  ||  !* filename )
201250            break ;
202251        if  (do_update (filename ))
203252            break ;
204253        free (filename );
205254    }
206255
256+     free (gp_buffer );
207257    rg_system_exit ();
208258}
0 commit comments