1 /* Copyright (C) 2010-2020 The RetroArch team
2 *
3 * ---------------------------------------------------------------------------------------
4 * The following license statement only applies to this file (disk_control_interface.c).
5 * ---------------------------------------------------------------------------------------
6 *
7 * Permission is hereby granted, free of charge,
8 * to any person obtaining a copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation the rights to
10 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
11 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
19 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 */
22
23 #include <string/stdstring.h>
24 #include <file/file_path.h>
25
26 #include "paths.h"
27 #include "retroarch.h"
28 #include "verbosity.h"
29 #include "msg_hash.h"
30
31 #include "disk_control_interface.h"
32
33 /*****************/
34 /* Configuration */
35 /*****************/
36
37 /* Sets all disk interface callback functions
38 * to NULL */
disk_control_reset_callback(disk_control_interface_t * disk_control)39 static void disk_control_reset_callback(
40 disk_control_interface_t *disk_control)
41 {
42 if (!disk_control)
43 return;
44
45 memset(&disk_control->cb, 0,
46 sizeof(struct retro_disk_control_ext_callback));
47 }
48
49 /* Set v0 disk interface callback functions */
disk_control_set_callback(disk_control_interface_t * disk_control,const struct retro_disk_control_callback * cb)50 void disk_control_set_callback(
51 disk_control_interface_t *disk_control,
52 const struct retro_disk_control_callback *cb)
53 {
54 if (!disk_control)
55 return;
56
57 disk_control_reset_callback(disk_control);
58
59 if (!cb)
60 return;
61
62 disk_control->cb.set_eject_state = cb->set_eject_state;
63 disk_control->cb.get_eject_state = cb->get_eject_state;
64 disk_control->cb.get_image_index = cb->get_image_index;
65 disk_control->cb.set_image_index = cb->set_image_index;
66 disk_control->cb.get_num_images = cb->get_num_images;
67 disk_control->cb.replace_image_index = cb->replace_image_index;
68 disk_control->cb.add_image_index = cb->add_image_index;
69 }
70
71 /* Set v1+ disk interface callback functions */
disk_control_set_ext_callback(disk_control_interface_t * disk_control,const struct retro_disk_control_ext_callback * cb)72 void disk_control_set_ext_callback(
73 disk_control_interface_t *disk_control,
74 const struct retro_disk_control_ext_callback *cb)
75 {
76 if (!disk_control)
77 return;
78
79 disk_control_reset_callback(disk_control);
80
81 if (!cb)
82 return;
83
84 disk_control->cb.set_eject_state = cb->set_eject_state;
85 disk_control->cb.get_eject_state = cb->get_eject_state;
86 disk_control->cb.get_image_index = cb->get_image_index;
87 disk_control->cb.set_image_index = cb->set_image_index;
88 disk_control->cb.get_num_images = cb->get_num_images;
89 disk_control->cb.replace_image_index = cb->replace_image_index;
90 disk_control->cb.add_image_index = cb->add_image_index;
91
92 disk_control->cb.set_initial_image = cb->set_initial_image;
93 disk_control->cb.get_image_path = cb->get_image_path;
94 disk_control->cb.get_image_label = cb->get_image_label;
95 }
96
97 /**********/
98 /* Status */
99 /**********/
100
101 /* Returns true if core supports basic disk
102 * control functionality
103 * - set_eject_state
104 * - get_eject_state
105 * - get_image_index
106 * - set_image_index
107 * - get_num_images */
disk_control_enabled(disk_control_interface_t * disk_control)108 bool disk_control_enabled(
109 disk_control_interface_t *disk_control)
110 {
111 if (!disk_control)
112 return false;
113
114 if (disk_control->cb.set_eject_state &&
115 disk_control->cb.get_eject_state &&
116 disk_control->cb.get_image_index &&
117 disk_control->cb.set_image_index &&
118 disk_control->cb.get_num_images)
119 return true;
120
121 return false;
122 }
123
124 /* Returns true if core supports disk append
125 * functionality
126 * - replace_image_index
127 * - add_image_index */
disk_control_append_enabled(disk_control_interface_t * disk_control)128 bool disk_control_append_enabled(
129 disk_control_interface_t *disk_control)
130 {
131 if (!disk_control)
132 return false;
133
134 if (disk_control->cb.replace_image_index &&
135 disk_control->cb.add_image_index)
136 return true;
137
138 return false;
139 }
140
141 /* Returns true if core supports image
142 * labels
143 * - get_image_label */
disk_control_image_label_enabled(disk_control_interface_t * disk_control)144 bool disk_control_image_label_enabled(
145 disk_control_interface_t *disk_control)
146 {
147 if (!disk_control || !disk_control->cb.get_image_label)
148 return false;
149 return true;
150 }
151
152 /* Returns true if core supports setting
153 * initial disk index
154 * - set_initial_image
155 * - get_image_path */
disk_control_initial_image_enabled(disk_control_interface_t * disk_control)156 bool disk_control_initial_image_enabled(
157 disk_control_interface_t *disk_control)
158 {
159 if (!disk_control)
160 return false;
161
162 if (disk_control->cb.set_initial_image &&
163 disk_control->cb.get_image_path)
164 return true;
165
166 return false;
167 }
168
169 /***********/
170 /* Getters */
171 /***********/
172
173 /* Returns true if disk is currently ejected */
disk_control_get_eject_state(disk_control_interface_t * disk_control)174 bool disk_control_get_eject_state(
175 disk_control_interface_t *disk_control)
176 {
177 if (!disk_control || !disk_control->cb.get_eject_state)
178 return false;
179 return disk_control->cb.get_eject_state();
180 }
181
182 /* Returns number of disk images registered
183 * by the core */
disk_control_get_num_images(disk_control_interface_t * disk_control)184 unsigned disk_control_get_num_images(
185 disk_control_interface_t *disk_control)
186 {
187 if (!disk_control || !disk_control->cb.get_num_images)
188 return 0;
189 return disk_control->cb.get_num_images();
190 }
191
192 /* Returns currently selected disk image index */
disk_control_get_image_index(disk_control_interface_t * disk_control)193 unsigned disk_control_get_image_index(
194 disk_control_interface_t *disk_control)
195 {
196 if (!disk_control || !disk_control->cb.get_image_index)
197 return 0;
198 return disk_control->cb.get_image_index();
199 }
200
201 /* Fetches core-provided disk image label
202 * (label is set to an empty string if core
203 * does not support image labels) */
disk_control_get_image_label(disk_control_interface_t * disk_control,unsigned index,char * label,size_t len)204 void disk_control_get_image_label(
205 disk_control_interface_t *disk_control,
206 unsigned index, char *label, size_t len)
207 {
208 if (!label || len < 1)
209 return;
210
211 if (!disk_control)
212 goto error;
213
214 if (!disk_control->cb.get_image_label)
215 goto error;
216
217 if (!disk_control->cb.get_image_label(index, label, len))
218 goto error;
219
220 return;
221
222 error:
223 label[0] = '\0';
224 }
225
226 /***********/
227 /* Setters */
228 /***********/
229
230 /* Generates an appropriate log/notification message
231 * for a disk index change event */
disk_control_get_index_set_msg(disk_control_interface_t * disk_control,unsigned num_images,unsigned index,bool success,unsigned * msg_duration,char * msg,size_t len)232 static void disk_control_get_index_set_msg(
233 disk_control_interface_t *disk_control,
234 unsigned num_images, unsigned index, bool success,
235 unsigned *msg_duration, char *msg, size_t len)
236 {
237 bool has_label = false;
238 char image_label[128];
239
240 image_label[0] = '\0';
241
242 if (!disk_control || !msg_duration || !msg || len < 1)
243 return;
244
245 /* Attempt to get image label */
246 if (index < num_images)
247 {
248 disk_control_get_image_label(
249 disk_control, index, image_label, sizeof(image_label));
250 has_label = !string_is_empty(image_label);
251 }
252
253 /* Get message duration
254 * > Default is 60
255 * > If a label is shown, then increase duration by 50%
256 * > For errors, duration is always 180 */
257 *msg_duration = success ?
258 (has_label ? 90 : 60) :
259 180;
260
261 /* Check whether image was inserted or removed */
262 if (index < num_images)
263 {
264 if (has_label)
265 snprintf(
266 msg, len, "%s: %u/%u - %s",
267 success ? msg_hash_to_str(MSG_SETTING_DISK_IN_TRAY) :
268 msg_hash_to_str(MSG_FAILED_TO_SET_DISK),
269 index + 1, num_images, image_label);
270 else
271 snprintf(
272 msg, len, "%s: %u/%u",
273 success ? msg_hash_to_str(MSG_SETTING_DISK_IN_TRAY) :
274 msg_hash_to_str(MSG_FAILED_TO_SET_DISK),
275 index + 1, num_images);
276 }
277 else
278 strlcpy(
279 msg,
280 success ? msg_hash_to_str(MSG_REMOVED_DISK_FROM_TRAY) :
281 msg_hash_to_str(MSG_FAILED_TO_REMOVE_DISK_FROM_TRAY),
282 len);
283 }
284
285 /* Sets the eject state of the virtual disk tray */
disk_control_set_eject_state(disk_control_interface_t * disk_control,bool eject,bool verbosity)286 bool disk_control_set_eject_state(
287 disk_control_interface_t *disk_control,
288 bool eject, bool verbosity)
289 {
290 bool error = false;
291 char msg[128];
292
293 msg[0] = '\0';
294
295 if (!disk_control || !disk_control->cb.set_eject_state)
296 return false;
297
298 /* Set eject state */
299 if (disk_control->cb.set_eject_state(eject))
300 snprintf(
301 msg, sizeof(msg), "%s %s",
302 eject ? msg_hash_to_str(MSG_DISK_EJECTED) :
303 msg_hash_to_str(MSG_DISK_CLOSED),
304 msg_hash_to_str(MSG_VIRTUAL_DISK_TRAY));
305 else
306 {
307 error = true;
308 snprintf(
309 msg, sizeof(msg), "%s %s %s",
310 msg_hash_to_str(MSG_FAILED_TO),
311 eject ? msg_hash_to_str(MSG_VIRTUAL_DISK_TRAY_EJECT) :
312 msg_hash_to_str(MSG_VIRTUAL_DISK_TRAY_CLOSE),
313 msg_hash_to_str(MSG_VIRTUAL_DISK_TRAY));
314 }
315
316 if (!string_is_empty(msg))
317 {
318 if (error)
319 RARCH_ERR("%s\n", msg);
320 else
321 RARCH_LOG("%s\n", msg);
322
323 /* Errors should always be displayed */
324 if (verbosity || error)
325 runloop_msg_queue_push(
326 msg, 1, error ? 180 : 60,
327 true, NULL,
328 MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
329 }
330
331 return !error;
332 }
333
334 /* Sets currently selected disk index
335 * NOTE: Will fail if disk is not currently ejected */
disk_control_set_index(disk_control_interface_t * disk_control,unsigned index,bool verbosity)336 bool disk_control_set_index(
337 disk_control_interface_t *disk_control,
338 unsigned index, bool verbosity)
339 {
340 bool error = false;
341 unsigned num_images = 0;
342 unsigned msg_duration = 0;
343 char msg[PATH_MAX_LENGTH];
344
345 msg[0] = '\0';
346
347 if (!disk_control)
348 return false;
349
350 if (!disk_control->cb.get_eject_state ||
351 !disk_control->cb.get_num_images ||
352 !disk_control->cb.set_image_index)
353 return false;
354
355 /* Ensure that disk is currently ejected */
356 if (!disk_control->cb.get_eject_state())
357 return false;
358
359 /* Get current number of disk images */
360 num_images = disk_control->cb.get_num_images();
361
362 /* Perform 'set index' action */
363 error = !disk_control->cb.set_image_index(index);
364
365 /* Get log/notification message */
366 disk_control_get_index_set_msg(
367 disk_control, num_images, index, !error,
368 &msg_duration, msg, sizeof(msg));
369
370 /* Output log/notification message */
371 if (!string_is_empty(msg))
372 {
373 if (error)
374 RARCH_ERR("%s\n", msg);
375 else
376 RARCH_LOG("%s\n", msg);
377
378 /* Errors should always be displayed */
379 if (verbosity || error)
380 runloop_msg_queue_push(
381 msg, 1, msg_duration,
382 true, NULL,
383 MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
384 }
385
386 /* If operation was successful, update disk
387 * index record (if enabled) */
388 if (!error && disk_control->record_enabled)
389 {
390 if (disk_control->cb.get_image_index &&
391 disk_control->cb.get_image_path)
392 {
393 bool image_path_valid = false;
394 unsigned new_image_index = 0;
395 char new_image_path[PATH_MAX_LENGTH];
396
397 new_image_path[0] = '\0';
398
399 /* Get current image index + path */
400 new_image_index = disk_control->cb.get_image_index();
401 image_path_valid = disk_control->cb.get_image_path(
402 new_image_index, new_image_path, sizeof(new_image_path));
403
404 if (image_path_valid)
405 disk_index_file_set(
406 &disk_control->index_record,
407 new_image_index, new_image_path);
408 else
409 disk_index_file_set(
410 &disk_control->index_record, 0, NULL);
411 }
412 }
413
414 return !error;
415 }
416
417 /* Increments selected disk index */
disk_control_set_index_next(disk_control_interface_t * disk_control,bool verbosity)418 bool disk_control_set_index_next(
419 disk_control_interface_t *disk_control,
420 bool verbosity)
421 {
422 unsigned num_images = 0;
423 unsigned image_index = 0;
424 bool disk_next_enable = false;
425
426 if (!disk_control)
427 return false;
428
429 if (!disk_control->cb.get_num_images ||
430 !disk_control->cb.get_image_index)
431 return false;
432
433 num_images = disk_control->cb.get_num_images();
434 image_index = disk_control->cb.get_image_index();
435 /* Would seem more sensible to check (num_images > 1)
436 * here, but seems we need to be able to cycle the
437 * same image for legacy reasons... */
438 disk_next_enable = (num_images > 0) && (num_images != UINT_MAX);
439
440 if (!disk_next_enable)
441 {
442 RARCH_ERR("%s.\n", msg_hash_to_str(MSG_GOT_INVALID_DISK_INDEX));
443 return false;
444 }
445
446 if (image_index < (num_images - 1))
447 image_index++;
448
449 return disk_control_set_index(disk_control, image_index, verbosity);
450 }
451
452 /* Decrements selected disk index */
disk_control_set_index_prev(disk_control_interface_t * disk_control,bool verbosity)453 bool disk_control_set_index_prev(
454 disk_control_interface_t *disk_control,
455 bool verbosity)
456 {
457 unsigned num_images = 0;
458 unsigned image_index = 0;
459 bool disk_prev_enable = false;
460
461 if (!disk_control)
462 return false;
463
464 if (!disk_control->cb.get_num_images ||
465 !disk_control->cb.get_image_index)
466 return false;
467
468 num_images = disk_control->cb.get_num_images();
469 image_index = disk_control->cb.get_image_index();
470 /* Would seem more sensible to check (num_images > 1)
471 * here, but seems we need to be able to cycle the
472 * same image for legacy reasons... */
473 disk_prev_enable = (num_images > 0);
474
475 if (!disk_prev_enable)
476 {
477 RARCH_ERR("%s.\n", msg_hash_to_str(MSG_GOT_INVALID_DISK_INDEX));
478 return false;
479 }
480
481 if (image_index > 0)
482 image_index--;
483
484 return disk_control_set_index(disk_control, image_index, verbosity);
485 }
486
487 /* Appends specified image file to disk image list */
disk_control_append_image(disk_control_interface_t * disk_control,const char * image_path)488 bool disk_control_append_image(
489 disk_control_interface_t *disk_control,
490 const char *image_path)
491 {
492 bool initial_disk_ejected = false;
493 unsigned initial_index = 0;
494 unsigned new_index = 0;
495 const char *image_filename = NULL;
496 struct retro_game_info info = {0};
497 char msg[128];
498
499 msg[0] = '\0';
500
501 /* Sanity check. If any of these fail then a
502 * frontend error has occurred - we will not
503 * deal with that here */
504 if (!disk_control)
505 return false;
506
507 if (!disk_control->cb.get_image_index ||
508 !disk_control->cb.get_num_images ||
509 !disk_control->cb.add_image_index ||
510 !disk_control->cb.replace_image_index ||
511 !disk_control->cb.get_eject_state)
512 return false;
513
514 if (string_is_empty(image_path))
515 return false;
516
517 image_filename = path_basename(image_path);
518
519 if (string_is_empty(image_filename))
520 return false;
521
522 /* Get initial disk eject state */
523 initial_disk_ejected = disk_control_get_eject_state(disk_control);
524
525 /* Cache initial image index */
526 initial_index = disk_control->cb.get_image_index();
527
528 /* If tray is currently closed, eject disk */
529 if (!initial_disk_ejected &&
530 !disk_control_set_eject_state(disk_control, true, false))
531 goto error;
532
533 /* Append image */
534 if (!disk_control->cb.add_image_index())
535 goto error;
536
537 new_index = disk_control->cb.get_num_images();
538 if (new_index < 1)
539 goto error;
540 new_index--;
541
542 info.path = image_path;
543 if (!disk_control->cb.replace_image_index(new_index, &info))
544 goto error;
545
546 /* Set new index */
547 if (!disk_control_set_index(disk_control, new_index, false))
548 goto error;
549
550 /* If tray was initially closed, insert disk
551 * (i.e. leave system in the state we found it) */
552 if (!initial_disk_ejected &&
553 !disk_control_set_eject_state(disk_control, false, false))
554 goto error;
555
556 /* Display log */
557 snprintf(
558 msg, sizeof(msg), "%s: %s",
559 msg_hash_to_str(MSG_APPENDED_DISK), image_filename);
560
561 RARCH_LOG("%s\n", msg);
562 /* This message should always be displayed, since
563 * the menu itself does not provide sufficient
564 * visual feedback */
565 runloop_msg_queue_push(
566 msg, 0, 120,
567 true, NULL,
568 MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
569
570 return true;
571
572 error:
573 /* If we reach this point then everything is
574 * broken and the disk control interface is
575 * in an undefined state. Try to restore some
576 * sanity by reinserting the original disk...
577 * NOTE: If this fails then it's game over -
578 * just display the error notification and
579 * hope for the best... */
580 if (!disk_control->cb.get_eject_state())
581 disk_control_set_eject_state(disk_control, true, false);
582 disk_control_set_index(disk_control, initial_index, false);
583 if (!initial_disk_ejected)
584 disk_control_set_eject_state(disk_control, false, false);
585
586 snprintf(
587 msg, sizeof(msg), "%s: %s",
588 msg_hash_to_str(MSG_FAILED_TO_APPEND_DISK), image_filename);
589
590 runloop_msg_queue_push(
591 msg, 0, 180,
592 true, NULL,
593 MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
594
595 return false;
596 }
597
598 /*****************************/
599 /* 'Initial index' functions */
600 /*****************************/
601
602 /* Attempts to set current core's initial disk index.
603 * > disk_control->record_enabled will be set to
604 * 'false' if core does not support initial
605 * index functionality
606 * > disk_control->index_record will be loaded
607 * from file (if an existing record is found)
608 * NOTE: Must be called immediately before
609 * loading content */
disk_control_set_initial_index(disk_control_interface_t * disk_control,const char * content_path,const char * dir_savefile)610 bool disk_control_set_initial_index(
611 disk_control_interface_t *disk_control,
612 const char *content_path,
613 const char *dir_savefile)
614 {
615 if (!disk_control)
616 return false;
617
618 if (string_is_empty(content_path))
619 goto error;
620
621 /* Check that 'initial index' functionality is enabled */
622 if (!disk_control->cb.set_initial_image ||
623 !disk_control->cb.get_num_images ||
624 !disk_control->cb.get_image_index ||
625 !disk_control->cb.get_image_path)
626 goto error;
627
628 /* Attempt to initialise disk index record (reading
629 * from disk, if file exists) */
630 disk_control->record_enabled = disk_index_file_init(
631 &disk_control->index_record,
632 content_path, dir_savefile);
633
634 /* If record is enabled and initial index is *not*
635 * zero, notify current core */
636 if (disk_control->record_enabled &&
637 (disk_control->index_record.image_index != 0))
638 {
639 if (!disk_control->cb.set_initial_image(
640 disk_control->index_record.image_index,
641 disk_control->index_record.image_path))
642 {
643 /* Note: We don't bother with an on-screen
644 * notification at this stage, since an error
645 * here may not matter (have to wait until
646 * disk index is verified) */
647 RARCH_ERR(
648 "Failed to set initial disk index: [%u] %s\n",
649 disk_control->index_record.image_index,
650 disk_control->index_record.image_path);
651 return false;
652 }
653 }
654
655 return true;
656
657 error:
658 disk_control->record_enabled = false;
659 return false;
660 }
661
662 /* Checks that initial index has been set correctly
663 * and provides user notification.
664 * > Sets disk_control->initial_num_images if
665 * if functionality is supported by core
666 * NOTE: Must be called immediately after
667 * loading content */
disk_control_verify_initial_index(disk_control_interface_t * disk_control,bool verbosity)668 bool disk_control_verify_initial_index(
669 disk_control_interface_t *disk_control,
670 bool verbosity)
671 {
672 bool success = false;
673 unsigned image_index = 0;
674 char image_path[PATH_MAX_LENGTH];
675
676 image_path[0] = '\0';
677
678 if (!disk_control)
679 return false;
680
681 /* If index record is disabled, can return immediately */
682 if (!disk_control->record_enabled)
683 return false;
684
685 /* Check that 'initial index' functionality is enabled */
686 if (!disk_control->cb.set_initial_image ||
687 !disk_control->cb.get_num_images ||
688 !disk_control->cb.get_image_index ||
689 !disk_control->cb.get_image_path)
690 return false;
691
692 /* Cache initial number of images
693 * (required for error checking when saving
694 * disk index file) */
695 disk_control->initial_num_images =
696 disk_control->cb.get_num_images();
697
698 /* Get current image index + path */
699 image_index = disk_control->cb.get_image_index();
700
701 if (disk_control->cb.get_image_path(
702 image_index, image_path, sizeof(image_path)))
703 {
704 /* Check whether index + path match set
705 * values
706 * > Note that if set index was zero and
707 * set path was empty, we ignore the path
708 * read here (since this corresponds to a
709 * 'first run', where no existing disk index
710 * file was present) */
711 if ((image_index == disk_control->index_record.image_index) &&
712 (string_is_equal(image_path, disk_control->index_record.image_path) ||
713 ((disk_control->index_record.image_index == 0) &&
714 string_is_empty(disk_control->index_record.image_path))))
715 success = true;
716 }
717
718 /* If current disk is incorrect, notify user */
719 if (!success)
720 {
721 RARCH_ERR(
722 "Failed to set initial disk index:\n> Expected [%u] %s\n> Detected [%u] %s\n",
723 disk_control->index_record.image_index + 1,
724 disk_control->index_record.image_path,
725 image_index + 1,
726 image_path);
727
728 /* Ignore 'verbosity' setting - errors should
729 * always be displayed */
730 runloop_msg_queue_push(
731 msg_hash_to_str(MSG_FAILED_TO_SET_INITIAL_DISK),
732 0, 60,
733 true, NULL,
734 MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
735
736 /* Since a failure here typically means that the
737 * original M3U content file has been altered,
738 * any existing disk index record file will be
739 * invalid. We therefore 'reset' and save the disk
740 * index record to prevent a repeat of the error on
741 * the next run */
742 disk_index_file_set(&disk_control->index_record, 0, NULL);
743 disk_index_file_save(&disk_control->index_record);
744 }
745 /* If current disk is correct and recorded image
746 * path is empty (i.e. first run), need to register
747 * current image path */
748 else if (string_is_empty(disk_control->index_record.image_path))
749 disk_index_file_set(
750 &disk_control->index_record, image_index, image_path);
751
752 /* Regardless of success/failure, notify user of
753 * current disk index *if* more than one disk
754 * is available */
755 if (disk_control->initial_num_images > 1)
756 {
757 unsigned msg_duration = 0;
758 char msg[PATH_MAX_LENGTH];
759
760 msg[0] = '\0';
761
762 disk_control_get_index_set_msg(
763 disk_control, disk_control->initial_num_images, image_index, true,
764 &msg_duration, msg, sizeof(msg));
765
766 RARCH_LOG("%s\n", msg);
767
768 /* Note: Do not flush message queue here, since
769 * it is likely other notifications will be
770 * generated before setting the disk index, and
771 * we do not want to 'overwrite' them */
772 if (verbosity)
773 runloop_msg_queue_push(
774 msg,
775 0, msg_duration,
776 false, NULL,
777 MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
778 }
779
780 return success;
781 }
782
783 /* Saves current disk index to file, if supported
784 * by current core */
disk_control_save_image_index(disk_control_interface_t * disk_control)785 bool disk_control_save_image_index(
786 disk_control_interface_t *disk_control)
787 {
788 if (!disk_control)
789 return false;
790
791 /* If index record is disabled, can return immediately */
792 if (!disk_control->record_enabled)
793 return false;
794
795 /* If core started with less than two disks,
796 * then a disk index record is unnecessary */
797 if (disk_control->initial_num_images < 2)
798 return false;
799
800 /* If current index is greater than initial
801 * number of disks then user has appended a
802 * disk and it is currently active. This setup
803 * *cannot* be restored, so cancel the file save */
804 if (disk_control->index_record.image_index >=
805 disk_control->initial_num_images)
806 return false;
807
808 /* Save record */
809 return disk_index_file_save(&disk_control->index_record);
810 }
811