1 /* RetroArch - A frontend for libretro.
2 * Copyright (C) 2011-2017 - Daniel De Matteis
3 * Copyright (C) 2014-2017 - Jean-André Santoni
4 * Copyright (C) 2016-2019 - Brad Parker
5 * Copyright (C) 2019 - James Leaver
6 *
7 * RetroArch is free software: you can redistribute it and/or modify it under the terms
8 * of the GNU General Public License as published by the Free Software Found-
9 * ation, either version 3 of the License, or (at your option) any later version.
10 *
11 * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
12 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
13 * PURPOSE. See the GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along with RetroArch.
16 * If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <stdlib.h>
20 #include <string.h>
21 #include <stdio.h>
22 #include <ctype.h>
23 #include <boolean.h>
24
25 #include <string/stdstring.h>
26 #include <file/file_path.h>
27 #include <net/net_http.h>
28 #include <streams/interface_stream.h>
29 #include <streams/file_stream.h>
30
31 #include "task_file_transfer.h"
32 #include "tasks_internal.h"
33
34 #include "../configuration.h"
35 #include "../retroarch.h"
36 #include "../command.h"
37 #include "../msg_hash.h"
38 #include "../verbosity.h"
39 #include "../core_updater_list.h"
40
41 #if defined(ANDROID)
42 #include "../file_path_special.h"
43 #include "../play_feature_delivery/play_feature_delivery.h"
44 #endif
45
46 #if defined(RARCH_INTERNAL) && defined(HAVE_MENU)
47 #include "../menu/menu_entries.h"
48 #endif
49
50 /* Get core updater list */
51 enum core_updater_list_status
52 {
53 CORE_UPDATER_LIST_BEGIN = 0,
54 CORE_UPDATER_LIST_WAIT,
55 CORE_UPDATER_LIST_END
56 };
57
58 typedef struct core_updater_list_handle
59 {
60 core_updater_list_t* core_list;
61 retro_task_t *http_task;
62 http_transfer_data_t *http_data;
63 enum core_updater_list_status status;
64 bool refresh_menu;
65 bool http_task_finished;
66 bool http_task_complete;
67 bool http_task_success;
68 } core_updater_list_handle_t;
69
70 /* Download core */
71 enum core_updater_download_status
72 {
73 CORE_UPDATER_DOWNLOAD_BEGIN = 0,
74 CORE_UPDATER_DOWNLOAD_START_BACKUP,
75 CORE_UPDATER_DOWNLOAD_WAIT_BACKUP,
76 CORE_UPDATER_DOWNLOAD_START_TRANSFER,
77 CORE_UPDATER_DOWNLOAD_WAIT_TRANSFER,
78 CORE_UPDATER_DOWNLOAD_WAIT_DECOMPRESS,
79 CORE_UPDATER_DOWNLOAD_END
80 };
81
82 typedef struct core_updater_download_handle
83 {
84 char *path_dir_libretro;
85 char *path_dir_core_assets;
86 char *remote_filename;
87 char *remote_core_path;
88 char *local_download_path;
89 char *local_core_path;
90 char *display_name;
91 retro_task_t *http_task;
92 retro_task_t *decompress_task;
93 retro_task_t *backup_task;
94 size_t auto_backup_history_size;
95 uint32_t local_crc;
96 uint32_t remote_crc;
97 enum core_updater_download_status status;
98 bool crc_match;
99 bool http_task_finished;
100 bool http_task_complete;
101 bool auto_backup;
102 bool decompress_task_finished;
103 bool decompress_task_complete;
104 bool backup_enabled;
105 } core_updater_download_handle_t;
106
107 /* Update installed cores */
108 enum update_installed_cores_status
109 {
110 UPDATE_INSTALLED_CORES_BEGIN = 0,
111 UPDATE_INSTALLED_CORES_WAIT_LIST,
112 UPDATE_INSTALLED_CORES_ITERATE,
113 UPDATE_INSTALLED_CORES_UPDATE_CORE,
114 UPDATE_INSTALLED_CORES_WAIT_DOWNLOAD,
115 UPDATE_INSTALLED_CORES_END
116 };
117
118 typedef struct update_installed_cores_handle
119 {
120 char *path_dir_libretro;
121 char *path_dir_core_assets;
122 core_updater_list_t* core_list;
123 retro_task_t *list_task;
124 retro_task_t *download_task;
125 size_t auto_backup_history_size;
126 size_t list_size;
127 size_t list_index;
128 size_t installed_index;
129 unsigned num_updated;
130 unsigned num_locked;
131 enum update_installed_cores_status status;
132 bool auto_backup;
133 } update_installed_cores_handle_t;
134
135 #if defined(ANDROID)
136 /* Play feature delivery core install */
137 enum play_feature_delivery_install_task_status
138 {
139 PLAY_FEATURE_DELIVERY_INSTALL_BEGIN = 0,
140 PLAY_FEATURE_DELIVERY_INSTALL_WAIT,
141 PLAY_FEATURE_DELIVERY_INSTALL_END
142 };
143
144 typedef struct play_feature_delivery_install_handle
145 {
146 char *core_filename;
147 char *local_core_path;
148 char *backup_core_path;
149 char *display_name;
150 enum play_feature_delivery_install_task_status status;
151 bool success;
152 bool core_already_installed;
153 } play_feature_delivery_install_handle_t;
154
155 /* Play feature delivery switch installed cores */
156 enum play_feature_delivery_switch_cores_task_status
157 {
158 PLAY_FEATURE_DELIVERY_SWITCH_CORES_BEGIN = 0,
159 PLAY_FEATURE_DELIVERY_SWITCH_CORES_ITERATE,
160 PLAY_FEATURE_DELIVERY_SWITCH_CORES_INSTALL_CORE,
161 PLAY_FEATURE_DELIVERY_SWITCH_CORES_WAIT_INSTALL,
162 PLAY_FEATURE_DELIVERY_SWITCH_CORES_END
163 };
164
165 typedef struct play_feature_delivery_switch_cores_handle
166 {
167 char *path_dir_libretro;
168 char *path_libretro_info;
169 char *error_msg;
170 core_updater_list_t* core_list;
171 retro_task_t *install_task;
172 size_t list_size;
173 size_t list_index;
174 size_t installed_index;
175 enum play_feature_delivery_switch_cores_task_status status;
176 } play_feature_delivery_switch_cores_handle_t;
177 #endif
178
179 /*********************/
180 /* Utility functions */
181 /*********************/
182
183 /* Returns CRC32 of specified core file */
task_core_updater_get_core_crc(const char * core_path)184 static uint32_t task_core_updater_get_core_crc(const char *core_path)
185 {
186 if (string_is_empty(core_path))
187 return 0;
188
189 if (path_is_valid(core_path))
190 {
191 /* Open core file */
192 intfstream_t *core_file = intfstream_open_file(
193 core_path, RETRO_VFS_FILE_ACCESS_READ,
194 RETRO_VFS_FILE_ACCESS_HINT_NONE);
195
196 if (core_file)
197 {
198 uint32_t crc = 0;
199
200 /* Get CRC value */
201 bool success = intfstream_get_crc(core_file, &crc);
202
203 /* Close core file */
204 intfstream_close(core_file);
205 free(core_file);
206 core_file = NULL;
207
208 if (success)
209 return crc;
210 }
211 }
212
213 return 0;
214 }
215
216 /*************************/
217 /* Get core updater list */
218 /*************************/
219
cb_http_task_core_updater_get_list(retro_task_t * task,void * task_data,void * user_data,const char * err)220 static void cb_http_task_core_updater_get_list(
221 retro_task_t *task, void *task_data,
222 void *user_data, const char *err)
223 {
224 http_transfer_data_t *data = (http_transfer_data_t*)task_data;
225 file_transfer_t *transf = (file_transfer_t*)user_data;
226 core_updater_list_handle_t *list_handle = NULL;
227 bool success = data && string_is_empty(err);
228
229 if (!transf)
230 goto finish;
231
232 list_handle = (core_updater_list_handle_t*)transf->user_data;
233
234 if (!list_handle)
235 goto finish;
236
237 task_set_data(task, NULL); /* going to pass ownership to list_handle */
238
239 list_handle->http_data = data;
240 list_handle->http_task_complete = true;
241 list_handle->http_task_success = success;
242
243
244 finish:
245
246 /* Log any error messages */
247 if (!success)
248 RARCH_ERR("[core updater] Download of core list '%s' failed: %s\n",
249 (transf ? transf->path: "unknown"),
250 (err ? err : "unknown"));
251
252 if (transf)
253 free(transf);
254 }
255
free_core_updater_list_handle(core_updater_list_handle_t * list_handle)256 static void free_core_updater_list_handle(
257 core_updater_list_handle_t *list_handle)
258 {
259 if (!list_handle)
260 return;
261
262 if (list_handle->http_data)
263 {
264 /* since we took onwership, we have to destroy it ourself */
265 if (list_handle->http_data->data)
266 free(list_handle->http_data->data);
267
268 free(list_handle->http_data);
269 }
270
271 free(list_handle);
272 list_handle = NULL;
273 }
274
task_core_updater_get_list_handler(retro_task_t * task)275 static void task_core_updater_get_list_handler(retro_task_t *task)
276 {
277 core_updater_list_handle_t *list_handle = NULL;
278
279 if (!task)
280 goto task_finished;
281
282 list_handle = (core_updater_list_handle_t*)task->state;
283
284 if (!list_handle)
285 goto task_finished;
286
287 if (task_get_cancelled(task))
288 goto task_finished;
289
290 switch (list_handle->status)
291 {
292 case CORE_UPDATER_LIST_BEGIN:
293 {
294 settings_t *settings = config_get_ptr();
295 file_transfer_t *transf = NULL;
296 char *tmp_url = NULL;
297 char buildbot_url[PATH_MAX_LENGTH];
298 const char *net_buildbot_url =
299 settings->paths.network_buildbot_url;
300
301 buildbot_url[0] = '\0';
302
303 /* Reset core updater list */
304 core_updater_list_reset(list_handle->core_list);
305
306 /* Get core listing URL */
307 if (!settings)
308 goto task_finished;
309
310 if (string_is_empty(net_buildbot_url))
311 goto task_finished;
312
313 fill_pathname_join(
314 buildbot_url,
315 net_buildbot_url,
316 ".index-extended",
317 sizeof(buildbot_url));
318
319 tmp_url = strdup(buildbot_url);
320 buildbot_url[0] = '\0';
321 net_http_urlencode_full(
322 buildbot_url, tmp_url, sizeof(buildbot_url));
323 if (tmp_url)
324 free(tmp_url);
325
326 if (string_is_empty(buildbot_url))
327 goto task_finished;
328
329 /* Configure file transfer object */
330 transf = (file_transfer_t*)calloc(1, sizeof(file_transfer_t));
331
332 if (!transf)
333 goto task_finished;
334
335 /* > Seems to be required - not sure why the
336 * underlying code is implemented like this... */
337 strlcpy(transf->path, buildbot_url, sizeof(transf->path));
338
339 transf->user_data = (void*)list_handle;
340
341 /* Push HTTP transfer task */
342 list_handle->http_task = (retro_task_t*)task_push_http_transfer_file(
343 buildbot_url, true, NULL,
344 cb_http_task_core_updater_get_list, transf);
345
346 /* Start waiting for HTTP transfer to complete */
347 list_handle->status = CORE_UPDATER_LIST_WAIT;
348 }
349 break;
350 case CORE_UPDATER_LIST_WAIT:
351 {
352 /* If HTTP task is NULL, then it either finished
353 * or an error occurred - in either case,
354 * just move on to the next state */
355 if (!list_handle->http_task)
356 list_handle->http_task_complete = true;
357 /* Otherwise, check if HTTP task is still running */
358 else if (!list_handle->http_task_finished)
359 {
360 list_handle->http_task_finished =
361 task_get_finished(list_handle->http_task);
362
363 /* If HTTP task is running, copy current
364 * progress value to *this* task */
365 if (!list_handle->http_task_finished)
366 task_set_progress(
367 task, task_get_progress(list_handle->http_task));
368 }
369
370 /* Wait for task_push_http_transfer_file()
371 * callback to trigger */
372 if (list_handle->http_task_complete)
373 list_handle->status = CORE_UPDATER_LIST_END;
374 }
375 break;
376 case CORE_UPDATER_LIST_END:
377 {
378 settings_t *settings = config_get_ptr();
379
380 /* Check whether HTTP task was successful */
381 if (list_handle->http_task_success)
382 {
383 /* Parse HTTP transfer data */
384 if (list_handle->http_data)
385 core_updater_list_parse_network_data(
386 list_handle->core_list,
387 settings->paths.directory_libretro,
388 settings->paths.path_libretro_info,
389 settings->paths.network_buildbot_url,
390 list_handle->http_data->data,
391 list_handle->http_data->len);
392 }
393 else
394 {
395 /* Notify user of error via task title */
396 task_free_title(task);
397 task_set_title(task, strdup(msg_hash_to_str(MSG_CORE_LIST_FAILED)));
398 }
399
400 /* Enable menu refresh, if required */
401 #if defined(RARCH_INTERNAL) && defined(HAVE_MENU)
402 if (list_handle->refresh_menu)
403 menu_entries_ctl(
404 MENU_ENTRIES_CTL_UNSET_REFRESH,
405 &list_handle->refresh_menu);
406 #endif
407 }
408 /* fall-through */
409 default:
410 task_set_progress(task, 100);
411 goto task_finished;
412 }
413
414 return;
415
416 task_finished:
417
418 if (task)
419 task_set_finished(task, true);
420
421 free_core_updater_list_handle(list_handle);
422 }
423
task_core_updater_get_list_finder(retro_task_t * task,void * user_data)424 static bool task_core_updater_get_list_finder(retro_task_t *task, void *user_data)
425 {
426 core_updater_list_handle_t *list_handle = NULL;
427
428 if (!task || !user_data)
429 return false;
430
431 if (task->handler != task_core_updater_get_list_handler)
432 return false;
433
434 list_handle = (core_updater_list_handle_t*)task->state;
435 if (!list_handle)
436 return false;
437
438 return ((uintptr_t)user_data == (uintptr_t)list_handle->core_list);
439 }
440
task_push_get_core_updater_list(core_updater_list_t * core_list,bool mute,bool refresh_menu)441 void *task_push_get_core_updater_list(
442 core_updater_list_t* core_list, bool mute, bool refresh_menu)
443 {
444 task_finder_data_t find_data;
445 retro_task_t *task = NULL;
446 core_updater_list_handle_t *list_handle = (core_updater_list_handle_t*)
447 calloc(1, sizeof(core_updater_list_handle_t));
448
449 #if defined(ANDROID)
450 /* Regular core updater is disabled in
451 * Play Store builds */
452 if (play_feature_delivery_enabled())
453 goto error;
454 #endif
455
456 /* Sanity check */
457 if (!core_list || !list_handle)
458 goto error;
459
460 /* Configure handle */
461 list_handle->core_list = core_list;
462 list_handle->refresh_menu = refresh_menu;
463 list_handle->http_task = NULL;
464 list_handle->http_task_finished = false;
465 list_handle->http_task_complete = false;
466 list_handle->http_task_success = false;
467 list_handle->http_data = NULL;
468 list_handle->status = CORE_UPDATER_LIST_BEGIN;
469
470 /* Concurrent downloads of the buildbot core listing
471 * to the same core_updater_list_t object are not
472 * allowed */
473 find_data.func = task_core_updater_get_list_finder;
474 find_data.userdata = (void*)core_list;
475
476 if (task_queue_find(&find_data))
477 goto error;
478
479 /* Create task */
480 task = task_init();
481
482 if (!task)
483 goto error;
484
485 /* Configure task */
486 task->handler = task_core_updater_get_list_handler;
487 task->state = list_handle;
488 task->mute = mute;
489 task->title = strdup(msg_hash_to_str(MSG_FETCHING_CORE_LIST));
490 task->alternative_look = true;
491 task->progress = 0;
492
493 /* Push task */
494 task_queue_push(task);
495
496 return task;
497
498 error:
499
500 /* Clean up task */
501 if (task)
502 {
503 free(task);
504 task = NULL;
505 }
506
507 /* Clean up handle */
508 free_core_updater_list_handle(list_handle);
509
510 return NULL;
511 }
512
513 /*****************/
514 /* Download core */
515 /*****************/
516
cb_task_core_updater_download(retro_task_t * task,void * task_data,void * user_data,const char * err)517 static void cb_task_core_updater_download(
518 retro_task_t *task, void *task_data,
519 void *user_data, const char *err)
520 {
521 /* Reload core info files
522 * > This must be done on the main thread */
523 command_event(CMD_EVENT_CORE_INFO_INIT, NULL);
524 }
525
cb_decompress_task_core_updater_download(retro_task_t * task,void * task_data,void * user_data,const char * err)526 static void cb_decompress_task_core_updater_download(
527 retro_task_t *task, void *task_data,
528 void *user_data, const char *err)
529 {
530 decompress_task_data_t *decompress_data =
531 (decompress_task_data_t*)task_data;
532 core_updater_download_handle_t *download_handle =
533 (core_updater_download_handle_t*)user_data;
534
535 /* Signal that decompression task is complete */
536 if (download_handle)
537 download_handle->decompress_task_complete = true;
538
539 /* Remove original archive file */
540 if (decompress_data)
541 {
542 if (!string_is_empty(decompress_data->source_file))
543 if (path_is_valid(decompress_data->source_file))
544 filestream_delete(decompress_data->source_file);
545
546 if (decompress_data->source_file)
547 free(decompress_data->source_file);
548
549 free(decompress_data);
550 }
551
552 /* Log any error messages */
553 if (!string_is_empty(err))
554 RARCH_ERR("[core updater] %s", err);
555 }
556
cb_http_task_core_updater_download(retro_task_t * task,void * task_data,void * user_data,const char * err)557 void cb_http_task_core_updater_download(
558 retro_task_t *task, void *task_data,
559 void *user_data, const char *err)
560 {
561 http_transfer_data_t *data = (http_transfer_data_t*)task_data;
562 file_transfer_t *transf = (file_transfer_t*)user_data;
563 core_updater_download_handle_t *download_handle = NULL;
564 char output_dir[PATH_MAX_LENGTH];
565
566 output_dir[0] = '\0';
567
568 if (!data || !transf)
569 goto finish;
570
571 if (!data->data || string_is_empty(transf->path))
572 goto finish;
573
574 download_handle = (core_updater_download_handle_t*)transf->user_data;
575
576 if (!download_handle)
577 goto finish;
578
579 /* Update download_handle task status
580 * NOTE: We set decompress_task_complete = true
581 * here to prevent any lock-ups in the event
582 * of errors (or lack of decompression support).
583 * decompress_task_complete will be set false
584 * if/when we actually call task_push_decompress() */
585 download_handle->http_task_complete = true;
586 download_handle->decompress_task_complete = true;
587
588 /* Create output directory, if required */
589 strlcpy(output_dir, transf->path, sizeof(output_dir));
590 path_basedir_wrapper(output_dir);
591
592 if (!path_mkdir(output_dir))
593 {
594 err = msg_hash_to_str(MSG_FAILED_TO_CREATE_THE_DIRECTORY);
595 goto finish;
596 }
597
598 #ifdef HAVE_COMPRESSION
599 /* If core file is an archive, make sure it is
600 * not being decompressed already (by another task) */
601 if (path_is_compressed_file(transf->path))
602 {
603 if (task_check_decompress(transf->path))
604 {
605 err = msg_hash_to_str(MSG_DECOMPRESSION_ALREADY_IN_PROGRESS);
606 goto finish;
607 }
608 }
609 #endif
610
611 /* Write core file to disk */
612 if (!filestream_write_file(transf->path, data->data, data->len))
613 {
614 err = "Write failed.";
615 goto finish;
616 }
617
618 #if defined(HAVE_COMPRESSION) && defined(HAVE_ZLIB)
619 /* Decompress core file, if required
620 * NOTE: If core is compressed and platform
621 * doesn't have compression support, then this
622 * whole thing falls apart...
623 * We assume that the build process is configured
624 * in such a way that this cannot happen... */
625 if (path_is_compressed_file(transf->path))
626 {
627 download_handle->decompress_task = (retro_task_t*)task_push_decompress(
628 transf->path, output_dir,
629 NULL, NULL, NULL,
630 cb_decompress_task_core_updater_download,
631 (void*)download_handle,
632 NULL, true);
633
634 if (!download_handle->decompress_task)
635 {
636 err = msg_hash_to_str(MSG_DECOMPRESSION_FAILED);
637 goto finish;
638 }
639
640 download_handle->decompress_task_complete = false;
641 }
642 #endif
643
644 finish:
645
646 /* Log any error messages */
647 if (!string_is_empty(err))
648 RARCH_ERR("[core updater] Download of '%s' failed: %s\n",
649 (transf ? transf->path: "unknown"), err);
650
651 if (transf)
652 free(transf);
653 }
654
free_core_updater_download_handle(core_updater_download_handle_t * download_handle)655 static void free_core_updater_download_handle(core_updater_download_handle_t *download_handle)
656 {
657 if (!download_handle)
658 return;
659
660 if (download_handle->path_dir_libretro)
661 free(download_handle->path_dir_libretro);
662
663 if (download_handle->path_dir_core_assets)
664 free(download_handle->path_dir_core_assets);
665
666 if (download_handle->remote_filename)
667 free(download_handle->remote_filename);
668
669 if (download_handle->remote_core_path)
670 free(download_handle->remote_core_path);
671
672 if (download_handle->local_download_path)
673 free(download_handle->local_download_path);
674
675 if (download_handle->local_core_path)
676 free(download_handle->local_core_path);
677
678 if (download_handle->display_name)
679 free(download_handle->display_name);
680
681 free(download_handle);
682 download_handle = NULL;
683 }
684
task_core_updater_download_handler(retro_task_t * task)685 static void task_core_updater_download_handler(retro_task_t *task)
686 {
687 core_updater_download_handle_t *download_handle = NULL;
688
689 if (!task)
690 goto task_finished;
691
692 download_handle = (core_updater_download_handle_t*)task->state;
693
694 if (!download_handle)
695 goto task_finished;
696
697 if (task_get_cancelled(task))
698 goto task_finished;
699
700 switch (download_handle->status)
701 {
702 case CORE_UPDATER_DOWNLOAD_BEGIN:
703 {
704 /* Get CRC of existing core, if required */
705 if (download_handle->local_crc == 0)
706 download_handle->local_crc = task_core_updater_get_core_crc(
707 download_handle->local_core_path);
708
709 /* Check whether existing core and remote core
710 * have the same CRC */
711 download_handle->crc_match = (download_handle->local_crc != 0) &&
712 (download_handle->local_crc == download_handle->remote_crc);
713
714 /* If CRC matches, end task immediately */
715 if (download_handle->crc_match)
716 {
717 download_handle->status = CORE_UPDATER_DOWNLOAD_END;
718 break;
719 }
720
721 /* If automatic backups are enabled and core is
722 * already installed, trigger a backup - otherwise,
723 * initialise download */
724 download_handle->backup_enabled = download_handle->auto_backup &&
725 path_is_valid(download_handle->local_core_path);
726
727 download_handle->status = download_handle->backup_enabled ?
728 CORE_UPDATER_DOWNLOAD_START_BACKUP :
729 CORE_UPDATER_DOWNLOAD_START_TRANSFER;
730 }
731 break;
732 case CORE_UPDATER_DOWNLOAD_START_BACKUP:
733 {
734 /* Request core backup */
735 download_handle->backup_task = (retro_task_t*)task_push_core_backup(
736 download_handle->local_core_path,
737 download_handle->display_name,
738 download_handle->local_crc, CORE_BACKUP_MODE_AUTO,
739 download_handle->auto_backup_history_size,
740 download_handle->path_dir_core_assets, true);
741
742 if (download_handle->backup_task)
743 {
744 char task_title[PATH_MAX_LENGTH];
745
746 task_title[0] = '\0';
747
748 /* Update task title */
749 task_free_title(task);
750
751 strlcpy(
752 task_title, msg_hash_to_str(MSG_BACKING_UP_CORE),
753 sizeof(task_title));
754 strlcat(task_title, download_handle->display_name, sizeof(task_title));
755
756 task_set_title(task, strdup(task_title));
757
758 /* Start waiting for backup to complete */
759 download_handle->status = CORE_UPDATER_DOWNLOAD_WAIT_BACKUP;
760 }
761 else
762 {
763 /* This cannot realistically happen...
764 * > If it does, just log an error and initialise
765 * download */
766 RARCH_ERR("[core updater] Failed to backup core: %s\n",
767 download_handle->local_core_path);
768 download_handle->backup_enabled = false;
769 download_handle->status = CORE_UPDATER_DOWNLOAD_START_TRANSFER;
770 }
771 }
772 break;
773 case CORE_UPDATER_DOWNLOAD_WAIT_BACKUP:
774 {
775 bool backup_complete = false;
776
777 /* > If task is running, check 'is finished'
778 * status
779 * > If task is NULL, then it is finished
780 * by definition */
781 if (download_handle->backup_task)
782 {
783 backup_complete = task_get_finished(download_handle->backup_task);
784
785 /* If backup task is running, copy current
786 * progress value to *this* task */
787 if (!backup_complete)
788 {
789 /* Backup accounts for first third of
790 * task progress */
791 int8_t progress = task_get_progress(download_handle->backup_task);
792
793 task_set_progress(task, (int8_t)(((float)progress * (1.0f / 3.0f)) + 0.5f));
794 }
795 }
796 else
797 backup_complete = true;
798
799 /* If backup is complete, initialise download */
800 if (backup_complete)
801 {
802 download_handle->backup_task = NULL;
803 download_handle->status = CORE_UPDATER_DOWNLOAD_START_TRANSFER;
804 }
805 }
806 break;
807 case CORE_UPDATER_DOWNLOAD_START_TRANSFER:
808 {
809 file_transfer_t *transf = NULL;
810 char task_title[PATH_MAX_LENGTH];
811
812 task_title[0] = '\0';
813
814 /* Configure file transfer object */
815 transf = (file_transfer_t*)calloc(1, sizeof(file_transfer_t));
816
817 if (!transf)
818 goto task_finished;
819
820 strlcpy(
821 transf->path, download_handle->local_download_path,
822 sizeof(transf->path));
823
824 transf->user_data = (void*)download_handle;
825
826 /* Push HTTP transfer task */
827 download_handle->http_task = (retro_task_t*)task_push_http_transfer_file(
828 download_handle->remote_core_path, true, NULL,
829 cb_http_task_core_updater_download, transf);
830
831 /* Update task title */
832 task_free_title(task);
833
834 strlcpy(
835 task_title, msg_hash_to_str(MSG_DOWNLOADING_CORE),
836 sizeof(task_title));
837 strlcat(task_title, download_handle->display_name, sizeof(task_title));
838
839 task_set_title(task, strdup(task_title));
840
841 /* Start waiting for HTTP transfer to complete */
842 download_handle->status = CORE_UPDATER_DOWNLOAD_WAIT_TRANSFER;
843 }
844 break;
845 case CORE_UPDATER_DOWNLOAD_WAIT_TRANSFER:
846 {
847 /* If HTTP task is NULL, then it either finished
848 * or an error occurred - in either case,
849 * just move on to the next state */
850 if (!download_handle->http_task)
851 download_handle->http_task_complete = true;
852 /* Otherwise, check if HTTP task is still running */
853 else if (!download_handle->http_task_finished)
854 {
855 download_handle->http_task_finished =
856 task_get_finished(download_handle->http_task);
857
858 /* If HTTP task is running, copy current
859 * progress value to *this* task */
860 if (!download_handle->http_task_finished)
861 {
862 /* > If backups are enabled, download accounts
863 * for second third of task progress
864 * > Otherwise, download accounts for first half
865 * of task progress */
866 int8_t progress = task_get_progress(download_handle->http_task);
867
868 if (download_handle->backup_enabled)
869 progress = (int8_t)(((float)progress * (1.0f / 3.0f)) + (100.0f / 3.0f) + 0.5f);
870 else
871 progress = progress >> 1;
872
873 task_set_progress(task, progress);
874 }
875 }
876
877 /* Wait for task_push_http_transfer_file()
878 * callback to trigger */
879 if (download_handle->http_task_complete)
880 {
881 char task_title[PATH_MAX_LENGTH];
882
883 task_title[0] = '\0';
884
885 /* Update task title */
886 task_free_title(task);
887
888 strlcpy(
889 task_title, msg_hash_to_str(MSG_EXTRACTING_CORE),
890 sizeof(task_title));
891 strlcat(task_title, download_handle->display_name, sizeof(task_title));
892
893 task_set_title(task, strdup(task_title));
894
895 /* Start waiting for file to be extracted */
896 download_handle->status = CORE_UPDATER_DOWNLOAD_WAIT_DECOMPRESS;
897 }
898 }
899 break;
900 case CORE_UPDATER_DOWNLOAD_WAIT_DECOMPRESS:
901 {
902 /* If decompression task is NULL, then it either
903 * finished or an error occurred - in either case,
904 * just move on to the next state */
905 if (!download_handle->decompress_task)
906 download_handle->decompress_task_complete = true;
907 /* Otherwise, check if decompression task is still
908 * running */
909 else if (!download_handle->decompress_task_finished)
910 {
911 download_handle->decompress_task_finished =
912 task_get_finished(download_handle->decompress_task);
913
914 /* If decompression task is running, copy
915 * current progress value to *this* task */
916 if (!download_handle->decompress_task_finished)
917 {
918 /* > If backups are enabled, decompression accounts
919 * for last third of task progress
920 * > Otherwise, decompression accounts for second
921 * half of task progress */
922 int8_t progress = task_get_progress(download_handle->decompress_task);
923
924 if (download_handle->backup_enabled)
925 progress = (int8_t)(((float)progress * (1.0f / 3.0f)) + (200.0f / 3.0f) + 0.5f);
926 else
927 progress = 50 + (progress >> 1);
928
929 task_set_progress(task, progress);
930 }
931 }
932
933 /* Wait for task_push_decompress()
934 * callback to trigger */
935 if (download_handle->decompress_task_complete)
936 download_handle->status = CORE_UPDATER_DOWNLOAD_END;
937 }
938 break;
939 case CORE_UPDATER_DOWNLOAD_END:
940 {
941 char task_title[PATH_MAX_LENGTH];
942
943 task_title[0] = '\0';
944
945 /* Set final task title */
946 task_free_title(task);
947
948 strlcpy(
949 task_title,
950 download_handle->crc_match ?
951 msg_hash_to_str(MSG_LATEST_CORE_INSTALLED) : msg_hash_to_str(MSG_CORE_INSTALLED),
952 sizeof(task_title));
953 strlcat(task_title, download_handle->display_name, sizeof(task_title));
954
955 task_set_title(task, strdup(task_title));
956 }
957 /* fall-through */
958 default:
959 task_set_progress(task, 100);
960 goto task_finished;
961 }
962
963 return;
964
965 task_finished:
966
967 if (task)
968 task_set_finished(task, true);
969
970 free_core_updater_download_handle(download_handle);
971 }
972
task_core_updater_download_finder(retro_task_t * task,void * user_data)973 static bool task_core_updater_download_finder(retro_task_t *task, void *user_data)
974 {
975 core_updater_download_handle_t *download_handle = NULL;
976
977 if (!task || !user_data)
978 return false;
979
980 if (task->handler != task_core_updater_download_handler)
981 return false;
982
983 download_handle = (core_updater_download_handle_t*)task->state;
984 if (!download_handle)
985 return false;
986
987 return string_is_equal((const char*)user_data, download_handle->remote_filename);
988 }
989
task_push_core_updater_download(core_updater_list_t * core_list,const char * filename,uint32_t crc,bool mute,bool auto_backup,size_t auto_backup_history_size,const char * path_dir_libretro,const char * path_dir_core_assets)990 void *task_push_core_updater_download(
991 core_updater_list_t* core_list,
992 const char *filename, uint32_t crc, bool mute,
993 bool auto_backup, size_t auto_backup_history_size,
994 const char *path_dir_libretro,
995 const char *path_dir_core_assets)
996 {
997 task_finder_data_t find_data;
998 char task_title[PATH_MAX_LENGTH];
999 char local_download_path[PATH_MAX_LENGTH];
1000 const core_updater_list_entry_t *list_entry = NULL;
1001 retro_task_t *task = NULL;
1002 core_updater_download_handle_t *download_handle = (core_updater_download_handle_t*)
1003 calloc(1, sizeof(core_updater_download_handle_t));
1004
1005 task_title[0] = '\0';
1006 local_download_path[0] = '\0';
1007
1008 #if defined(ANDROID)
1009 /* Regular core updater is disabled in
1010 * Play Store builds */
1011 if (play_feature_delivery_enabled())
1012 goto error;
1013 #endif
1014
1015 /* Sanity check */
1016 if (!core_list ||
1017 string_is_empty(filename) ||
1018 !download_handle)
1019 goto error;
1020
1021 /* Get core updater list entry */
1022 if (!core_updater_list_get_filename(
1023 core_list, filename, &list_entry))
1024 goto error;
1025
1026 if (string_is_empty(list_entry->remote_core_path) ||
1027 string_is_empty(list_entry->local_core_path) ||
1028 string_is_empty(list_entry->display_name))
1029 goto error;
1030
1031 /* Check whether core is locked
1032 * > Have to set validate_path to 'false' here,
1033 * since this may not run on the main thread
1034 * > Validation is not required anyway, since core
1035 * updater list provides 'sane' core paths */
1036 if (core_info_get_core_lock(list_entry->local_core_path, false))
1037 {
1038 RARCH_ERR("[core updater] Update disabled - core is locked: %s\n",
1039 list_entry->local_core_path);
1040
1041 /* If task is not muted, generate notification */
1042 if (!mute)
1043 {
1044 char msg[PATH_MAX_LENGTH];
1045
1046 msg[0] = '\0';
1047
1048 strlcpy(msg, msg_hash_to_str(MSG_CORE_UPDATE_DISABLED), sizeof(msg));
1049 strlcat(msg, list_entry->display_name, sizeof(msg));
1050
1051 runloop_msg_queue_push(msg, 1, 100, true,
1052 NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
1053 }
1054
1055 goto error;
1056 }
1057
1058 /* Get local file download path */
1059 if (string_is_empty(path_dir_libretro))
1060 goto error;
1061
1062 fill_pathname_join(
1063 local_download_path,
1064 path_dir_libretro,
1065 list_entry->remote_filename,
1066 sizeof(local_download_path));
1067
1068 /* Configure handle */
1069 download_handle->auto_backup = auto_backup;
1070 download_handle->auto_backup_history_size = auto_backup_history_size;
1071 download_handle->path_dir_libretro = strdup(path_dir_libretro);
1072 download_handle->path_dir_core_assets = string_is_empty(path_dir_core_assets) ? NULL : strdup(path_dir_core_assets);
1073 download_handle->remote_filename = strdup(list_entry->remote_filename);
1074 download_handle->remote_core_path = strdup(list_entry->remote_core_path);
1075 download_handle->local_download_path = strdup(local_download_path);
1076 download_handle->local_core_path = strdup(list_entry->local_core_path);
1077 download_handle->display_name = strdup(list_entry->display_name);
1078 download_handle->local_crc = crc;
1079 download_handle->remote_crc = list_entry->crc;
1080 download_handle->crc_match = false;
1081 download_handle->http_task = NULL;
1082 download_handle->http_task_finished = false;
1083 download_handle->http_task_complete = false;
1084 download_handle->decompress_task = NULL;
1085 download_handle->decompress_task_finished = false;
1086 download_handle->decompress_task_complete = false;
1087 download_handle->backup_enabled = false;
1088 download_handle->backup_task = NULL;
1089 download_handle->status = CORE_UPDATER_DOWNLOAD_BEGIN;
1090
1091 /* Concurrent downloads of the same file are not allowed */
1092 find_data.func = task_core_updater_download_finder;
1093 find_data.userdata = (void*)download_handle->remote_filename;
1094
1095 if (task_queue_find(&find_data))
1096 goto error;
1097
1098 /* Create task */
1099 task = task_init();
1100
1101 if (!task)
1102 goto error;
1103
1104 /* Configure task */
1105 strlcpy(
1106 task_title, msg_hash_to_str(MSG_UPDATING_CORE),
1107 sizeof(task_title));
1108 strlcat(task_title, download_handle->display_name, sizeof(task_title));
1109
1110 task->handler = task_core_updater_download_handler;
1111 task->state = download_handle;
1112 task->mute = mute;
1113 task->title = strdup(task_title);
1114 task->alternative_look = true;
1115 task->progress = 0;
1116 task->callback = cb_task_core_updater_download;
1117
1118 /* Push task */
1119 task_queue_push(task);
1120
1121 return task;
1122
1123 error:
1124
1125 /* Clean up task */
1126 if (task)
1127 {
1128 free(task);
1129 task = NULL;
1130 }
1131
1132 /* Clean up handle */
1133 free_core_updater_download_handle(download_handle);
1134
1135 return NULL;
1136 }
1137
1138 /**************************/
1139 /* Update installed cores */
1140 /**************************/
1141
free_update_installed_cores_handle(update_installed_cores_handle_t * update_installed_handle)1142 static void free_update_installed_cores_handle(
1143 update_installed_cores_handle_t *update_installed_handle)
1144 {
1145 if (!update_installed_handle)
1146 return;
1147
1148 if (update_installed_handle->path_dir_libretro)
1149 free(update_installed_handle->path_dir_libretro);
1150
1151 if (update_installed_handle->path_dir_core_assets)
1152 free(update_installed_handle->path_dir_core_assets);
1153
1154 core_updater_list_free(update_installed_handle->core_list);
1155
1156 free(update_installed_handle);
1157 update_installed_handle = NULL;
1158 }
1159
task_update_installed_cores_handler(retro_task_t * task)1160 static void task_update_installed_cores_handler(retro_task_t *task)
1161 {
1162 update_installed_cores_handle_t *update_installed_handle = NULL;
1163
1164 if (!task)
1165 goto task_finished;
1166
1167 update_installed_handle = (update_installed_cores_handle_t*)task->state;
1168
1169 if (!update_installed_handle)
1170 goto task_finished;
1171
1172 if (task_get_cancelled(task))
1173 goto task_finished;
1174
1175 switch (update_installed_handle->status)
1176 {
1177 case UPDATE_INSTALLED_CORES_BEGIN:
1178 /* Request buildbot core list */
1179 update_installed_handle->list_task = (retro_task_t*)
1180 task_push_get_core_updater_list(
1181 update_installed_handle->core_list,
1182 true, false);
1183
1184 /* If push failed, go to end
1185 * (error will message will be displayed when
1186 * final task title is set) */
1187 if (!update_installed_handle->list_task)
1188 update_installed_handle->status = UPDATE_INSTALLED_CORES_END;
1189 else
1190 update_installed_handle->status = UPDATE_INSTALLED_CORES_WAIT_LIST;
1191 break;
1192 case UPDATE_INSTALLED_CORES_WAIT_LIST:
1193 {
1194 bool list_available = false;
1195
1196 /* > If task is running, check 'is finished'
1197 * status
1198 * > If task is NULL, then it is finished
1199 * by definition */
1200 if (update_installed_handle->list_task)
1201 list_available = task_get_finished(update_installed_handle->list_task);
1202 else
1203 list_available = true;
1204
1205 /* If list is available, make sure it isn't empty
1206 * (error will message will be displayed when
1207 * final task title is set) */
1208 if (list_available)
1209 {
1210 update_installed_handle->list_size =
1211 core_updater_list_size(update_installed_handle->core_list);
1212
1213 if (update_installed_handle->list_size < 1)
1214 update_installed_handle->status = UPDATE_INSTALLED_CORES_END;
1215 else
1216 update_installed_handle->status = UPDATE_INSTALLED_CORES_ITERATE;
1217 }
1218 }
1219 break;
1220 case UPDATE_INSTALLED_CORES_ITERATE:
1221 {
1222 const core_updater_list_entry_t *list_entry = NULL;
1223 bool core_installed = false;
1224
1225 /* Check whether we have reached the end
1226 * of the list */
1227 if (update_installed_handle->list_index >= update_installed_handle->list_size)
1228 {
1229 update_installed_handle->status = UPDATE_INSTALLED_CORES_END;
1230 break;
1231 }
1232
1233 /* Check whether current core is installed */
1234 if (core_updater_list_get_index(
1235 update_installed_handle->core_list,
1236 update_installed_handle->list_index,
1237 &list_entry))
1238 {
1239 if (path_is_valid(list_entry->local_core_path))
1240 {
1241 core_installed = true;
1242 update_installed_handle->installed_index =
1243 update_installed_handle->list_index;
1244 update_installed_handle->status =
1245 UPDATE_INSTALLED_CORES_UPDATE_CORE;
1246 }
1247 }
1248
1249 /* Update progress display */
1250 task_free_title(task);
1251
1252 if (core_installed)
1253 {
1254 char task_title[PATH_MAX_LENGTH];
1255
1256 task_title[0] = '\0';
1257
1258 strlcpy(
1259 task_title, msg_hash_to_str(MSG_CHECKING_CORE),
1260 sizeof(task_title));
1261 strlcat(task_title, list_entry->display_name, sizeof(task_title));
1262
1263 task_set_title(task, strdup(task_title));
1264 }
1265 else
1266 task_set_title(task, strdup(msg_hash_to_str(MSG_SCANNING_CORES)));
1267
1268 task_set_progress(task,
1269 (update_installed_handle->list_index * 100) /
1270 update_installed_handle->list_size);
1271
1272 /* Increment list index */
1273 update_installed_handle->list_index++;
1274 }
1275 break;
1276 case UPDATE_INSTALLED_CORES_UPDATE_CORE:
1277 {
1278 const core_updater_list_entry_t *list_entry = NULL;
1279 uint32_t local_crc;
1280
1281 /* Get list entry
1282 * > In the event of an error, just return
1283 * to UPDATE_INSTALLED_CORES_ITERATE state */
1284 if (!core_updater_list_get_index(
1285 update_installed_handle->core_list,
1286 update_installed_handle->installed_index,
1287 &list_entry))
1288 {
1289 update_installed_handle->status = UPDATE_INSTALLED_CORES_ITERATE;
1290 break;
1291 }
1292
1293 /* Check whether core is locked
1294 * > Have to set validate_path to 'false' here,
1295 * since this does not run on the main thread
1296 * > Validation is not required anyway, since core
1297 * updater list provides 'sane' core paths */
1298 if (core_info_get_core_lock(list_entry->local_core_path, false))
1299 {
1300 RARCH_LOG("[core updater] Skipping locked core: %s\n",
1301 list_entry->display_name);
1302
1303 /* Core update is disabled
1304 * > Just increment 'locked cores' counter and
1305 * return to UPDATE_INSTALLED_CORES_ITERATE state */
1306 update_installed_handle->num_locked++;
1307 update_installed_handle->status = UPDATE_INSTALLED_CORES_ITERATE;
1308 break;
1309 }
1310
1311 /* Get CRC of existing core */
1312 local_crc = task_core_updater_get_core_crc(
1313 list_entry->local_core_path);
1314
1315 /* Check whether existing core and remote core
1316 * have the same CRC
1317 * > If CRC matches, then core is already the most
1318 * recent version - just return to
1319 * UPDATE_INSTALLED_CORES_ITERATE state */
1320 if ((local_crc != 0) && (local_crc == list_entry->crc))
1321 {
1322 update_installed_handle->status = UPDATE_INSTALLED_CORES_ITERATE;
1323 break;
1324 }
1325
1326 /* Existing core is not the most recent version
1327 * > Request download */
1328 update_installed_handle->download_task = (retro_task_t*)
1329 task_push_core_updater_download(
1330 update_installed_handle->core_list,
1331 list_entry->remote_filename,
1332 local_crc, true,
1333 update_installed_handle->auto_backup,
1334 update_installed_handle->auto_backup_history_size,
1335 update_installed_handle->path_dir_libretro,
1336 update_installed_handle->path_dir_core_assets);
1337
1338 /* Again, if an error occurred, just return to
1339 * UPDATE_INSTALLED_CORES_ITERATE state */
1340 if (!update_installed_handle->download_task)
1341 update_installed_handle->status = UPDATE_INSTALLED_CORES_ITERATE;
1342 else
1343 {
1344 char task_title[PATH_MAX_LENGTH];
1345
1346 task_title[0] = '\0';
1347
1348 /* Update task title */
1349 task_free_title(task);
1350
1351 strlcpy(
1352 task_title, msg_hash_to_str(MSG_UPDATING_CORE),
1353 sizeof(task_title));
1354 strlcat(task_title, list_entry->display_name, sizeof(task_title));
1355
1356 task_set_title(task, strdup(task_title));
1357
1358 /* Increment 'updated cores' counter */
1359 update_installed_handle->num_updated++;
1360
1361 /* Wait for download to complete */
1362 update_installed_handle->status = UPDATE_INSTALLED_CORES_WAIT_DOWNLOAD;
1363 }
1364 }
1365 break;
1366 case UPDATE_INSTALLED_CORES_WAIT_DOWNLOAD:
1367 {
1368 bool download_complete = false;
1369
1370 /* > If task is running, check 'is finished'
1371 * status
1372 * > If task is NULL, then it is finished
1373 * by definition */
1374 if (update_installed_handle->download_task)
1375 download_complete = task_get_finished(update_installed_handle->download_task);
1376 else
1377 download_complete = true;
1378
1379 /* If download is complete, return to
1380 * UPDATE_INSTALLED_CORES_ITERATE state */
1381 if (download_complete)
1382 {
1383 update_installed_handle->download_task = NULL;
1384 update_installed_handle->status = UPDATE_INSTALLED_CORES_ITERATE;
1385 }
1386 }
1387 break;
1388 case UPDATE_INSTALLED_CORES_END:
1389 {
1390 /* Set final task title */
1391 task_free_title(task);
1392
1393 /* > Check whether core list was fetched
1394 * successfully */
1395 if (update_installed_handle->list_size > 0)
1396 {
1397 char task_title[PATH_MAX_LENGTH];
1398
1399 task_title[0] = '\0';
1400
1401 /* > Generate final status message based on number
1402 * of cores that were updated/locked */
1403 if (update_installed_handle->num_updated > 0)
1404 {
1405 if (update_installed_handle->num_locked > 0)
1406 snprintf(
1407 task_title, sizeof(task_title), "%s [%s%u, %s%u]",
1408 msg_hash_to_str(MSG_ALL_CORES_UPDATED),
1409 msg_hash_to_str(MSG_NUM_CORES_UPDATED),
1410 update_installed_handle->num_updated,
1411 msg_hash_to_str(MSG_NUM_CORES_LOCKED),
1412 update_installed_handle->num_locked);
1413 else
1414 snprintf(
1415 task_title, sizeof(task_title), "%s [%s%u]",
1416 msg_hash_to_str(MSG_ALL_CORES_UPDATED),
1417 msg_hash_to_str(MSG_NUM_CORES_UPDATED),
1418 update_installed_handle->num_updated);
1419 }
1420 else if (update_installed_handle->num_locked > 0)
1421 snprintf(
1422 task_title, sizeof(task_title), "%s [%s%u]",
1423 msg_hash_to_str(MSG_ALL_CORES_UPDATED),
1424 msg_hash_to_str(MSG_NUM_CORES_LOCKED),
1425 update_installed_handle->num_locked);
1426 else
1427 strlcpy(task_title, msg_hash_to_str(MSG_ALL_CORES_UPDATED),
1428 sizeof(task_title));
1429
1430 task_set_title(task, strdup(task_title));
1431 }
1432 else
1433 task_set_title(task, strdup(msg_hash_to_str(MSG_CORE_LIST_FAILED)));
1434 }
1435 /* fall-through */
1436 default:
1437 task_set_progress(task, 100);
1438 goto task_finished;
1439 }
1440
1441 return;
1442
1443 task_finished:
1444
1445 if (task)
1446 task_set_finished(task, true);
1447
1448 free_update_installed_cores_handle(update_installed_handle);
1449 }
1450
task_update_installed_cores_finder(retro_task_t * task,void * user_data)1451 static bool task_update_installed_cores_finder(retro_task_t *task, void *user_data)
1452 {
1453 if (!task)
1454 return false;
1455
1456 if (task->handler == task_update_installed_cores_handler)
1457 return true;
1458
1459 return false;
1460 }
1461
task_push_update_installed_cores(bool auto_backup,size_t auto_backup_history_size,const char * path_dir_libretro,const char * path_dir_core_assets)1462 void task_push_update_installed_cores(
1463 bool auto_backup, size_t auto_backup_history_size,
1464 const char *path_dir_libretro,
1465 const char *path_dir_core_assets)
1466 {
1467 task_finder_data_t find_data;
1468 retro_task_t *task = NULL;
1469 update_installed_cores_handle_t *update_installed_handle =
1470 (update_installed_cores_handle_t*)
1471 calloc(1, sizeof(update_installed_cores_handle_t));
1472
1473 #if defined(ANDROID)
1474 /* Regular core updater is disabled in
1475 * Play Store builds */
1476 if (play_feature_delivery_enabled())
1477 goto error;
1478 #endif
1479
1480 /* Sanity check */
1481 if (!update_installed_handle ||
1482 string_is_empty(path_dir_libretro))
1483 goto error;
1484
1485 /* Configure handle */
1486 update_installed_handle->auto_backup = auto_backup;
1487 update_installed_handle->auto_backup_history_size = auto_backup_history_size;
1488 update_installed_handle->path_dir_libretro = strdup(path_dir_libretro);
1489 update_installed_handle->path_dir_core_assets = string_is_empty(path_dir_core_assets) ?
1490 NULL : strdup(path_dir_core_assets);
1491 update_installed_handle->core_list = core_updater_list_init();
1492 update_installed_handle->list_task = NULL;
1493 update_installed_handle->download_task = NULL;
1494 update_installed_handle->list_size = 0;
1495 update_installed_handle->list_index = 0;
1496 update_installed_handle->installed_index = 0;
1497 update_installed_handle->num_updated = 0;
1498 update_installed_handle->num_locked = 0;
1499 update_installed_handle->status = UPDATE_INSTALLED_CORES_BEGIN;
1500
1501 if (!update_installed_handle->core_list)
1502 goto error;
1503
1504 /* Only one instance of this task may run at a time */
1505 find_data.func = task_update_installed_cores_finder;
1506 find_data.userdata = NULL;
1507
1508 if (task_queue_find(&find_data))
1509 goto error;
1510
1511 /* Create task */
1512 task = task_init();
1513
1514 if (!task)
1515 goto error;
1516
1517 /* Configure task */
1518 task->handler = task_update_installed_cores_handler;
1519 task->state = update_installed_handle;
1520 task->title = strdup(msg_hash_to_str(MSG_FETCHING_CORE_LIST));
1521 task->alternative_look = true;
1522 task->progress = 0;
1523
1524 /* Push task */
1525 task_queue_push(task);
1526
1527 return;
1528
1529 error:
1530
1531 /* Clean up task */
1532 if (task)
1533 {
1534 free(task);
1535 task = NULL;
1536 }
1537
1538 /* Clean up handle */
1539 free_update_installed_cores_handle(update_installed_handle);
1540 }
1541
1542 #if defined(ANDROID)
1543 /**************************************/
1544 /* Play feature delivery core install */
1545 /**************************************/
1546
free_play_feature_delivery_install_handle(play_feature_delivery_install_handle_t * pfd_install_handle)1547 static void free_play_feature_delivery_install_handle(
1548 play_feature_delivery_install_handle_t *pfd_install_handle)
1549 {
1550 if (!pfd_install_handle)
1551 return;
1552
1553 if (pfd_install_handle->core_filename)
1554 free(pfd_install_handle->core_filename);
1555
1556 if (pfd_install_handle->local_core_path)
1557 free(pfd_install_handle->local_core_path);
1558
1559 if (pfd_install_handle->backup_core_path)
1560 free(pfd_install_handle->backup_core_path);
1561
1562 if (pfd_install_handle->display_name)
1563 free(pfd_install_handle->display_name);
1564
1565 free(pfd_install_handle);
1566 pfd_install_handle = NULL;
1567 }
1568
task_play_feature_delivery_core_install_handler(retro_task_t * task)1569 static void task_play_feature_delivery_core_install_handler(retro_task_t *task)
1570 {
1571 play_feature_delivery_install_handle_t *pfd_install_handle = NULL;
1572
1573 if (!task)
1574 goto task_finished;
1575
1576 pfd_install_handle = (play_feature_delivery_install_handle_t*)task->state;
1577
1578 if (!pfd_install_handle)
1579 goto task_finished;
1580
1581 if (task_get_cancelled(task))
1582 goto task_finished;
1583
1584 switch (pfd_install_handle->status)
1585 {
1586 case PLAY_FEATURE_DELIVERY_INSTALL_BEGIN:
1587 {
1588 /* Check whether core has already been
1589 * installed via play feature delivery */
1590 if (play_feature_delivery_core_installed(
1591 pfd_install_handle->core_filename))
1592 {
1593 pfd_install_handle->success = true;
1594 pfd_install_handle->core_already_installed = true;
1595 pfd_install_handle->status =
1596 PLAY_FEATURE_DELIVERY_INSTALL_END;
1597 break;
1598 }
1599
1600 /* If core is already installed via other
1601 * means, must remove it before attempting
1602 * play feature delivery transaction */
1603 if (path_is_valid(pfd_install_handle->local_core_path))
1604 {
1605 char backup_core_path[PATH_MAX_LENGTH];
1606 bool backup_successful = false;
1607
1608 backup_core_path[0] = '\0';
1609
1610 /* Have to create a backup, in case install
1611 * process fails
1612 * > Note: since only one install task can
1613 * run at a time, a UID is not required */
1614
1615 /* Generate backup file name */
1616 strlcpy(backup_core_path, pfd_install_handle->local_core_path,
1617 sizeof(backup_core_path));
1618 strlcat(backup_core_path, FILE_PATH_BACKUP_EXTENSION,
1619 sizeof(backup_core_path));
1620
1621 if (!string_is_empty(backup_core_path))
1622 {
1623 int ret;
1624
1625 /* If an old backup file exists (i.e. leftovers
1626 * from a mid-task crash/user exit), delete it */
1627 if (path_is_valid(backup_core_path))
1628 filestream_delete(backup_core_path);
1629
1630 /* Attempt to rename core file */
1631 ret = filestream_rename(
1632 pfd_install_handle->local_core_path,
1633 backup_core_path);
1634
1635 if (!ret)
1636 {
1637 /* Success - cache backup file name */
1638 pfd_install_handle->backup_core_path = strdup(backup_core_path);
1639 backup_successful = true;
1640 }
1641 }
1642
1643 /* If backup failed, all we can do is delete
1644 * the existing core file... */
1645 if (!backup_successful &&
1646 path_is_valid(pfd_install_handle->local_core_path))
1647 filestream_delete(pfd_install_handle->local_core_path);
1648 }
1649
1650 /* Start download */
1651 if (play_feature_delivery_download(
1652 pfd_install_handle->core_filename))
1653 pfd_install_handle->status = PLAY_FEATURE_DELIVERY_INSTALL_WAIT;
1654 else
1655 pfd_install_handle->status = PLAY_FEATURE_DELIVERY_INSTALL_END;
1656 }
1657 break;
1658 case PLAY_FEATURE_DELIVERY_INSTALL_WAIT:
1659 {
1660 bool install_active;
1661 enum play_feature_delivery_install_status install_status;
1662 unsigned install_progress;
1663 char task_title[PATH_MAX_LENGTH];
1664
1665 task_title[0] = '\0';
1666
1667 /* Get current install status */
1668 install_active = play_feature_delivery_download_status(
1669 &install_status, &install_progress);
1670
1671 /* In all cases, update task progress */
1672 task_set_progress(task, install_progress);
1673
1674 /* Interpret status */
1675 switch (install_status)
1676 {
1677 case PLAY_FEATURE_DELIVERY_INSTALLED:
1678 pfd_install_handle->success = true;
1679 pfd_install_handle->status = PLAY_FEATURE_DELIVERY_INSTALL_END;
1680 break;
1681 case PLAY_FEATURE_DELIVERY_FAILED:
1682 pfd_install_handle->status = PLAY_FEATURE_DELIVERY_INSTALL_END;
1683 break;
1684 case PLAY_FEATURE_DELIVERY_DOWNLOADING:
1685 task_free_title(task);
1686 strlcpy(task_title, msg_hash_to_str(MSG_DOWNLOADING_CORE),
1687 sizeof(task_title));
1688 strlcat(task_title, pfd_install_handle->display_name,
1689 sizeof(task_title));
1690 task_set_title(task, strdup(task_title));
1691 break;
1692 case PLAY_FEATURE_DELIVERY_INSTALLING:
1693 task_free_title(task);
1694 strlcpy(task_title, msg_hash_to_str(MSG_INSTALLING_CORE),
1695 sizeof(task_title));
1696 strlcat(task_title, pfd_install_handle->display_name,
1697 sizeof(task_title));
1698 task_set_title(task, strdup(task_title));
1699 break;
1700 default:
1701 break;
1702 }
1703
1704 /* If install is inactive, end task (regardless
1705 * of status) */
1706 if (!install_active)
1707 pfd_install_handle->status = PLAY_FEATURE_DELIVERY_INSTALL_END;
1708 }
1709 break;
1710 case PLAY_FEATURE_DELIVERY_INSTALL_END:
1711 {
1712 const char *msg_str = msg_hash_to_str(MSG_CORE_INSTALL_FAILED);
1713 char task_title[PATH_MAX_LENGTH];
1714
1715 task_title[0] = '\0';
1716
1717 /* Set final task title */
1718 task_free_title(task);
1719
1720 if (pfd_install_handle->success)
1721 msg_str = pfd_install_handle->core_already_installed ?
1722 msg_hash_to_str(MSG_LATEST_CORE_INSTALLED) :
1723 msg_hash_to_str(MSG_CORE_INSTALLED);
1724
1725 strlcpy(task_title, msg_str, sizeof(task_title));
1726 strlcat(task_title, pfd_install_handle->display_name,
1727 sizeof(task_title));
1728
1729 task_set_title(task, strdup(task_title));
1730
1731 /* Check whether a core backup file was created */
1732 if (!string_is_empty(pfd_install_handle->backup_core_path) &&
1733 path_is_valid(pfd_install_handle->backup_core_path))
1734 {
1735 /* If install was successful, delete backup */
1736 if (pfd_install_handle->success)
1737 filestream_delete(pfd_install_handle->backup_core_path);
1738 else
1739 {
1740 /* Otherwise, attempt to restore backup */
1741 int ret = filestream_rename(
1742 pfd_install_handle->backup_core_path,
1743 pfd_install_handle->local_core_path);
1744
1745 /* If restore failed, all we can do is attempt
1746 * to delete the backup... */
1747 if (ret && path_is_valid(pfd_install_handle->backup_core_path))
1748 filestream_delete(pfd_install_handle->backup_core_path);
1749 }
1750 }
1751
1752 /* If task is muted and install failed, set
1753 * error string (allows status to be checked
1754 * externally) */
1755 if (!pfd_install_handle->success &&
1756 task_get_mute(task))
1757 task_set_error(task, strdup(task_title));
1758 }
1759 /* fall-through */
1760 default:
1761 task_set_progress(task, 100);
1762 goto task_finished;
1763 }
1764
1765 return;
1766
1767 task_finished:
1768
1769 if (task)
1770 task_set_finished(task, true);
1771
1772 free_play_feature_delivery_install_handle(pfd_install_handle);
1773 }
1774
task_play_feature_delivery_core_install_finder(retro_task_t * task,void * user_data)1775 static bool task_play_feature_delivery_core_install_finder(
1776 retro_task_t *task, void *user_data)
1777 {
1778 if (!task)
1779 return false;
1780
1781 if (task->handler == task_play_feature_delivery_core_install_handler)
1782 return true;
1783
1784 return false;
1785 }
1786
task_push_play_feature_delivery_core_install(core_updater_list_t * core_list,const char * filename,bool mute)1787 void *task_push_play_feature_delivery_core_install(
1788 core_updater_list_t* core_list,
1789 const char *filename,
1790 bool mute)
1791 {
1792 task_finder_data_t find_data;
1793 char task_title[PATH_MAX_LENGTH];
1794 const core_updater_list_entry_t *list_entry = NULL;
1795 retro_task_t *task = NULL;
1796 play_feature_delivery_install_handle_t *pfd_install_handle = (play_feature_delivery_install_handle_t*)
1797 calloc(1, sizeof(play_feature_delivery_install_handle_t));
1798
1799 task_title[0] = '\0';
1800
1801 /* Sanity check */
1802 if (!core_list ||
1803 string_is_empty(filename) ||
1804 !pfd_install_handle ||
1805 !play_feature_delivery_enabled())
1806 goto error;
1807
1808 /* Get core updater list entry */
1809 if (!core_updater_list_get_filename(
1810 core_list, filename, &list_entry))
1811 goto error;
1812
1813 if (string_is_empty(list_entry->local_core_path) ||
1814 string_is_empty(list_entry->display_name))
1815 goto error;
1816
1817 /* Only one core may be downloaded at a time */
1818 find_data.func = task_play_feature_delivery_core_install_finder;
1819 find_data.userdata = NULL;
1820
1821 if (task_queue_find(&find_data))
1822 goto error;
1823
1824 /* Configure handle */
1825 pfd_install_handle->core_filename = strdup(list_entry->remote_filename);
1826 pfd_install_handle->local_core_path = strdup(list_entry->local_core_path);
1827 pfd_install_handle->backup_core_path = NULL;
1828 pfd_install_handle->display_name = strdup(list_entry->display_name);
1829 pfd_install_handle->success = false;
1830 pfd_install_handle->core_already_installed = false;
1831 pfd_install_handle->status = PLAY_FEATURE_DELIVERY_INSTALL_BEGIN;
1832
1833 /* Create task */
1834 task = task_init();
1835
1836 if (!task)
1837 goto error;
1838
1839 /* Configure task */
1840 strlcpy(task_title, msg_hash_to_str(MSG_UPDATING_CORE),
1841 sizeof(task_title));
1842 strlcat(task_title, pfd_install_handle->display_name,
1843 sizeof(task_title));
1844
1845 task->handler = task_play_feature_delivery_core_install_handler;
1846 task->state = pfd_install_handle;
1847 task->mute = mute;
1848 task->title = strdup(task_title);
1849 task->alternative_look = true;
1850 task->progress = 0;
1851 task->callback = cb_task_core_updater_download;
1852
1853 /* Install process may involve the *deletion*
1854 * of an existing core file. If core is
1855 * already running, must therefore unload it
1856 * to prevent undefined behaviour */
1857 if (rarch_ctl(RARCH_CTL_IS_CORE_LOADED, (void*)list_entry->local_core_path))
1858 command_event(CMD_EVENT_UNLOAD_CORE, NULL);
1859
1860 /* Push task */
1861 task_queue_push(task);
1862
1863 return task;
1864
1865 error:
1866
1867 /* Clean up task */
1868 if (task)
1869 {
1870 free(task);
1871 task = NULL;
1872 }
1873
1874 /* Clean up handle */
1875 free_play_feature_delivery_install_handle(pfd_install_handle);
1876
1877 return NULL;
1878 }
1879
1880 /************************************************/
1881 /* Play feature delivery switch installed cores */
1882 /************************************************/
1883
free_play_feature_delivery_switch_cores_handle(play_feature_delivery_switch_cores_handle_t * pfd_switch_cores_handle)1884 static void free_play_feature_delivery_switch_cores_handle(
1885 play_feature_delivery_switch_cores_handle_t *pfd_switch_cores_handle)
1886 {
1887 if (!pfd_switch_cores_handle)
1888 return;
1889
1890 if (pfd_switch_cores_handle->path_dir_libretro)
1891 free(pfd_switch_cores_handle->path_dir_libretro);
1892
1893 if (pfd_switch_cores_handle->path_libretro_info)
1894 free(pfd_switch_cores_handle->path_libretro_info);
1895
1896 if (pfd_switch_cores_handle->error_msg)
1897 free(pfd_switch_cores_handle->error_msg);
1898
1899 core_updater_list_free(pfd_switch_cores_handle->core_list);
1900
1901 free(pfd_switch_cores_handle);
1902 pfd_switch_cores_handle = NULL;
1903 }
1904
task_play_feature_delivery_switch_cores_handler(retro_task_t * task)1905 static void task_play_feature_delivery_switch_cores_handler(retro_task_t *task)
1906 {
1907 play_feature_delivery_switch_cores_handle_t *pfd_switch_cores_handle = NULL;
1908
1909 if (!task)
1910 goto task_finished;
1911
1912 pfd_switch_cores_handle = (play_feature_delivery_switch_cores_handle_t*)task->state;
1913
1914 if (!pfd_switch_cores_handle)
1915 goto task_finished;
1916
1917 if (task_get_cancelled(task))
1918 goto task_finished;
1919
1920 switch (pfd_switch_cores_handle->status)
1921 {
1922 case PLAY_FEATURE_DELIVERY_SWITCH_CORES_BEGIN:
1923 {
1924 /* Query available cores
1925 * Note: It should never be possible for this
1926 * function (or the subsequent parsing of its
1927 * output) to fail. We handle error conditions
1928 * regardless, but there is no need to perform
1929 * detailed checking - just report any problems
1930 * to the user as a generic 'failed to retrieve
1931 * core list' error */
1932 struct string_list *available_cores =
1933 play_feature_delivery_available_cores();
1934 bool success = false;
1935
1936 if (!available_cores)
1937 {
1938 pfd_switch_cores_handle->status =
1939 PLAY_FEATURE_DELIVERY_SWITCH_CORES_END;
1940 break;
1941 }
1942
1943 /* Populate core updater list */
1944 success = core_updater_list_parse_pfd_data(
1945 pfd_switch_cores_handle->core_list,
1946 pfd_switch_cores_handle->path_dir_libretro,
1947 pfd_switch_cores_handle->path_libretro_info,
1948 available_cores);
1949
1950 string_list_free(available_cores);
1951
1952 /* Cache list size */
1953 if (success)
1954 pfd_switch_cores_handle->list_size =
1955 core_updater_list_size(pfd_switch_cores_handle->core_list);
1956
1957 if (pfd_switch_cores_handle->list_size < 1)
1958 pfd_switch_cores_handle->status =
1959 PLAY_FEATURE_DELIVERY_SWITCH_CORES_END;
1960 else
1961 pfd_switch_cores_handle->status =
1962 PLAY_FEATURE_DELIVERY_SWITCH_CORES_ITERATE;
1963 }
1964 break;
1965 case PLAY_FEATURE_DELIVERY_SWITCH_CORES_ITERATE:
1966 {
1967 const core_updater_list_entry_t *list_entry = NULL;
1968 bool core_installed = false;
1969
1970 /* Check whether we have reached the end
1971 * of the list */
1972 if (pfd_switch_cores_handle->list_index >=
1973 pfd_switch_cores_handle->list_size)
1974 {
1975 pfd_switch_cores_handle->status =
1976 PLAY_FEATURE_DELIVERY_SWITCH_CORES_END;
1977 break;
1978 }
1979
1980 /* Check whether current core is installed */
1981 if (core_updater_list_get_index(
1982 pfd_switch_cores_handle->core_list,
1983 pfd_switch_cores_handle->list_index,
1984 &list_entry) &&
1985 path_is_valid(list_entry->local_core_path))
1986 {
1987 core_installed = true;
1988 pfd_switch_cores_handle->installed_index =
1989 pfd_switch_cores_handle->list_index;
1990 pfd_switch_cores_handle->status =
1991 PLAY_FEATURE_DELIVERY_SWITCH_CORES_INSTALL_CORE;
1992 }
1993
1994 /* Update progress display */
1995 task_free_title(task);
1996
1997 if (core_installed)
1998 {
1999 char task_title[PATH_MAX_LENGTH];
2000
2001 task_title[0] = '\0';
2002
2003 strlcpy(task_title, msg_hash_to_str(MSG_CHECKING_CORE),
2004 sizeof(task_title));
2005 strlcat(task_title, list_entry->display_name,
2006 sizeof(task_title));
2007
2008 task_set_title(task, strdup(task_title));
2009 }
2010 else
2011 task_set_title(task, strdup(msg_hash_to_str(MSG_SCANNING_CORES)));
2012
2013 task_set_progress(task,
2014 (pfd_switch_cores_handle->list_index * 100) /
2015 pfd_switch_cores_handle->list_size);
2016
2017 /* Increment list index */
2018 pfd_switch_cores_handle->list_index++;
2019 }
2020 break;
2021 case PLAY_FEATURE_DELIVERY_SWITCH_CORES_INSTALL_CORE:
2022 {
2023 const core_updater_list_entry_t *list_entry = NULL;
2024
2025 /* Get list entry
2026 * > In the event of an error, just return
2027 * to PLAY_FEATURE_DELIVERY_SWITCH_CORES_ITERATE
2028 * state */
2029 if (!core_updater_list_get_index(
2030 pfd_switch_cores_handle->core_list,
2031 pfd_switch_cores_handle->installed_index,
2032 &list_entry))
2033 {
2034 pfd_switch_cores_handle->status =
2035 PLAY_FEATURE_DELIVERY_SWITCH_CORES_ITERATE;
2036 break;
2037 }
2038
2039 /* Check whether core is already installed via
2040 * play feature delivery */
2041 if (play_feature_delivery_core_installed(
2042 list_entry->remote_filename))
2043 {
2044 pfd_switch_cores_handle->status =
2045 PLAY_FEATURE_DELIVERY_SWITCH_CORES_ITERATE;
2046 break;
2047 }
2048
2049 /* Existing core is not installed via
2050 * play feature delivery
2051 * > Request installation/replacement */
2052 pfd_switch_cores_handle->install_task = (retro_task_t*)
2053 task_push_play_feature_delivery_core_install(
2054 pfd_switch_cores_handle->core_list,
2055 list_entry->remote_filename,
2056 true);
2057
2058 /* Again, if an error occurred, just return to
2059 * PLAY_FEATURE_DELIVERY_SWITCH_CORES_ITERATE
2060 * state */
2061 if (!pfd_switch_cores_handle->install_task)
2062 pfd_switch_cores_handle->status =
2063 PLAY_FEATURE_DELIVERY_SWITCH_CORES_ITERATE;
2064 else
2065 {
2066 char task_title[PATH_MAX_LENGTH];
2067
2068 task_title[0] = '\0';
2069
2070 /* Update task title */
2071 task_free_title(task);
2072
2073 strlcpy(task_title, msg_hash_to_str(MSG_UPDATING_CORE),
2074 sizeof(task_title));
2075 strlcat(task_title, list_entry->display_name,
2076 sizeof(task_title));
2077
2078 task_set_title(task, strdup(task_title));
2079
2080 /* Wait for installation to complete */
2081 pfd_switch_cores_handle->status =
2082 PLAY_FEATURE_DELIVERY_SWITCH_CORES_WAIT_INSTALL;
2083 }
2084 }
2085 break;
2086 case PLAY_FEATURE_DELIVERY_SWITCH_CORES_WAIT_INSTALL:
2087 {
2088 bool install_complete = false;
2089 const char* error_msg = NULL;
2090
2091 /* > If task is running, check 'is finished' status
2092 * > If task is NULL, then it is finished by
2093 * definition */
2094 if (pfd_switch_cores_handle->install_task)
2095 {
2096 error_msg = task_get_error(
2097 pfd_switch_cores_handle->install_task);
2098 install_complete = task_get_finished(
2099 pfd_switch_cores_handle->install_task);
2100 }
2101 else
2102 install_complete = true;
2103
2104 /* Check for installation errors
2105 * > These should be considered 'serious', and
2106 * will trigger the task to end early */
2107 if (!string_is_empty(error_msg))
2108 {
2109 pfd_switch_cores_handle->error_msg = strdup(error_msg);
2110 pfd_switch_cores_handle->install_task = NULL;
2111 pfd_switch_cores_handle->status =
2112 PLAY_FEATURE_DELIVERY_SWITCH_CORES_END;
2113 break;
2114 }
2115
2116 /* If installation is complete, return to
2117 * PLAY_FEATURE_DELIVERY_SWITCH_CORES_ITERATE
2118 * state */
2119 if (install_complete)
2120 {
2121 pfd_switch_cores_handle->install_task = NULL;
2122 pfd_switch_cores_handle->status =
2123 PLAY_FEATURE_DELIVERY_SWITCH_CORES_ITERATE;
2124 }
2125 }
2126 break;
2127 case PLAY_FEATURE_DELIVERY_SWITCH_CORES_END:
2128 {
2129 const char *task_title = msg_hash_to_str(MSG_CORE_LIST_FAILED);
2130
2131 /* Set final task title */
2132 task_free_title(task);
2133
2134 /* > Check whether core list was generated
2135 * successfully */
2136 if (pfd_switch_cores_handle->list_size > 0)
2137 {
2138 /* Check whether any installation errors occurred */
2139 if (!string_is_empty(pfd_switch_cores_handle->error_msg))
2140 task_title = pfd_switch_cores_handle->error_msg;
2141 else
2142 task_title = msg_hash_to_str(MSG_ALL_CORES_SWITCHED_PFD);
2143 }
2144
2145 task_set_title(task, strdup(task_title));
2146 }
2147 /* fall-through */
2148 default:
2149 task_set_progress(task, 100);
2150 goto task_finished;
2151 }
2152
2153 return;
2154
2155 task_finished:
2156
2157 if (task)
2158 task_set_finished(task, true);
2159
2160 free_play_feature_delivery_switch_cores_handle(pfd_switch_cores_handle);
2161 }
2162
task_play_feature_delivery_switch_cores_finder(retro_task_t * task,void * user_data)2163 static bool task_play_feature_delivery_switch_cores_finder(
2164 retro_task_t *task, void *user_data)
2165 {
2166 if (!task)
2167 return false;
2168
2169 if (task->handler == task_play_feature_delivery_switch_cores_handler)
2170 return true;
2171
2172 return false;
2173 }
2174
task_push_play_feature_delivery_switch_installed_cores(const char * path_dir_libretro,const char * path_libretro_info)2175 void task_push_play_feature_delivery_switch_installed_cores(
2176 const char *path_dir_libretro,
2177 const char *path_libretro_info)
2178 {
2179 task_finder_data_t find_data;
2180 retro_task_t *task = NULL;
2181 play_feature_delivery_switch_cores_handle_t *pfd_switch_cores_handle =
2182 (play_feature_delivery_switch_cores_handle_t*)
2183 calloc(1, sizeof(play_feature_delivery_switch_cores_handle_t));
2184
2185 /* Sanity check */
2186 if (string_is_empty(path_dir_libretro) ||
2187 string_is_empty(path_libretro_info) ||
2188 !pfd_switch_cores_handle ||
2189 !play_feature_delivery_enabled())
2190 goto error;
2191
2192 /* Only one instance of this task my run at a time */
2193 find_data.func = task_play_feature_delivery_switch_cores_finder;
2194 find_data.userdata = NULL;
2195
2196 if (task_queue_find(&find_data))
2197 goto error;
2198
2199 /* Configure handle */
2200 pfd_switch_cores_handle->path_dir_libretro = strdup(path_dir_libretro);
2201 pfd_switch_cores_handle->path_libretro_info = strdup(path_libretro_info);
2202 pfd_switch_cores_handle->error_msg = NULL;
2203 pfd_switch_cores_handle->core_list = core_updater_list_init();
2204 pfd_switch_cores_handle->install_task = NULL;
2205 pfd_switch_cores_handle->list_size = 0;
2206 pfd_switch_cores_handle->list_index = 0;
2207 pfd_switch_cores_handle->installed_index = 0;
2208 pfd_switch_cores_handle->status = PLAY_FEATURE_DELIVERY_SWITCH_CORES_BEGIN;
2209
2210 if (!pfd_switch_cores_handle->core_list)
2211 goto error;
2212
2213 /* Create task */
2214 task = task_init();
2215
2216 if (!task)
2217 goto error;
2218
2219 /* Configure task */
2220 task->handler = task_play_feature_delivery_switch_cores_handler;
2221 task->state = pfd_switch_cores_handle;
2222 task->title = strdup(msg_hash_to_str(MSG_SCANNING_CORES));
2223 task->alternative_look = true;
2224 task->progress = 0;
2225
2226 /* Push task */
2227 task_queue_push(task);
2228
2229 return;
2230
2231 error:
2232
2233 /* Clean up task */
2234 if (task)
2235 {
2236 free(task);
2237 task = NULL;
2238 }
2239
2240 /* Clean up handle */
2241 free_play_feature_delivery_switch_cores_handle(pfd_switch_cores_handle);
2242 }
2243
2244 #endif
2245