14
14
// CONFIG_SPI_FLASH_DANGEROUS_WRITE_ALLOWED
15
15
16
16
#define MAX_PARTITIONS (24) // ESP_PARTITION_TABLE_MAX_ENTRIES
17
+ #define ALIGN_BLOCK (val , alignment ) ((int)(((val) + (alignment - 1)) / alignment) * alignment)
17
18
18
19
#define RETRO_GO_IMG_MAGIC "RG_IMG_0"
19
20
typedef struct
@@ -27,155 +28,204 @@ typedef struct
27
28
char reserved [156 ];
28
29
} img_info_t ;
29
30
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
-
44
31
typedef struct
45
32
{
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 ;
49
37
38
+ static size_t gp_buffer_size = 0x20000 ;
39
+ static void * gp_buffer = NULL ;
50
40
static rg_app_t * app ;
51
- // static esp_partition_info_t device_partition_table[ESP_PARTITION_TABLE_MAX_ENTRIES];
52
41
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 )
54
61
{
55
62
char message_buffer [256 ];
56
- esp_partition_info_t partition_table [16 ]; // ESP_PARTITION_TABLE_MAX_ENTRIES
57
- int num_partitions = 0 ;
58
63
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 ;
71
65
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
73
68
img_info .version [sizeof (img_info .version ) - 1 ] = 0 ; // Just in case
74
69
img_info .target [sizeof (img_info .target ) - 1 ] = 0 ; // Just in case
75
70
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
77
72
{
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 ;
79
88
}
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))
81
90
{
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; \
89
130
}
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 )
91
138
{
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" );
93
151
}
94
152
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
+ }
99
161
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 };
102
172
size_t queue_count = 0 ;
173
+ char message_buffer [256 ];
174
+ FILE * fp = NULL ;
103
175
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 ;
104
184
// 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 )
106
186
{
107
187
const esp_partition_info_t * src = & partition_table [i ];
108
188
const esp_partition_t * dst = esp_partition_find_first (src -> type , ESP_PARTITION_SUBTYPE_ANY , (char * )src -> label );
109
189
110
190
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 );
112
192
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 );
114
194
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 );
116
196
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 );
118
198
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 );
120
200
else
121
201
{
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 ;
125
209
}
126
210
}
127
211
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 );
149
213
150
- for ( size_t i = 0 ; i < queue_count ; ++ i )
214
+ if ( queue_count == 0 )
151
215
{
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;
174
218
}
175
- free (buffer );
176
219
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 );
178
224
return true;
225
+ fail :
226
+ if (fp )
227
+ fclose (fp );
228
+ return false;
179
229
}
180
230
181
231
void app_main (void )
@@ -187,22 +237,22 @@ void app_main(void)
187
237
.isLauncher = true,
188
238
});
189
239
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" );
195
243
196
244
// const char *filename = app->romPath;
197
245
while (true)
198
246
{
199
247
char * filename = rg_gui_file_picker ("Select update" , RG_BASE_PATH_UPDATES , NULL , true, true);
248
+ rg_display_clear (C_BLACK );
200
249
if (!filename || !* filename )
201
250
break ;
202
251
if (do_update (filename ))
203
252
break ;
204
253
free (filename );
205
254
}
206
255
256
+ free (gp_buffer );
207
257
rg_system_exit ();
208
258
}
0 commit comments