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