disk_control_interface.c 25 KB

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