From 00397a33f044cbc11eb20dd33692e9609ab473f3 Mon Sep 17 00:00:00 2001 From: jdgleaver Date: Wed, 22 Jan 2020 17:07:50 +0000 Subject: [PATCH] (Disk Control Interface) Add support for saving/restoring last used disk in playlists + code overhaul --- Makefile.common | 4 +- core.h | 5 +- disk_control_interface.c | 774 ++++++++++++++++++++++++++++++++++ disk_control_interface.h | 178 ++++++++ disk_index_file.c | 523 +++++++++++++++++++++++ disk_index_file.h | 76 ++++ file_path_special.h | 3 +- file_path_str.c | 3 + griffin/griffin.c | 8 +- intl/msg_hash_us.h | 20 + menu/cbs/menu_cbs_get_value.c | 19 +- menu/cbs/menu_cbs_ok.c | 10 +- menu/menu_displaylist.c | 50 +-- msg_hash.h | 4 + retroarch.c | 392 ++++------------- runtime_file.c | 2 +- 16 files changed, 1698 insertions(+), 373 deletions(-) create mode 100644 disk_control_interface.c create mode 100644 disk_control_interface.h create mode 100644 disk_index_file.c create mode 100644 disk_index_file.h diff --git a/Makefile.common b/Makefile.common index 586aa37f34..be4a7be9e4 100644 --- a/Makefile.common +++ b/Makefile.common @@ -243,6 +243,7 @@ OBJ += \ $(LIBRETRO_COMM_DIR)/file/config_file.o \ $(LIBRETRO_COMM_DIR)/file/config_file_userdata.o \ runtime_file.o \ + disk_index_file.o \ tasks/task_screenshot.o \ tasks/task_powerstate.o \ $(LIBRETRO_COMM_DIR)/gfx/scaler/scaler.o \ @@ -261,7 +262,8 @@ OBJ += \ performance_counters.o \ verbosity.o \ $(LIBRETRO_COMM_DIR)/playlists/label_sanitization.o \ - manual_content_scan.o + manual_content_scan.o \ + disk_control_interface.o ifeq ($(HAVE_AUDIOMIXER), 1) DEFINES += -DHAVE_AUDIOMIXER diff --git a/core.h b/core.h index c2c090b72e..35d7961ca0 100644 --- a/core.h +++ b/core.h @@ -25,6 +25,7 @@ #include "core_type.h" #include "input/input_defines.h" +#include "disk_control_interface.h" RETRO_BEGIN_DECLS @@ -67,8 +68,8 @@ typedef struct rarch_system_info bool supports_vfs; - struct retro_disk_control_ext_callback disk_control_cb; - struct retro_location_callback location_cb; + disk_control_interface_t disk_control; + struct retro_location_callback location_cb; struct { diff --git a/disk_control_interface.c b/disk_control_interface.c new file mode 100644 index 0000000000..2042afb94c --- /dev/null +++ b/disk_control_interface.c @@ -0,0 +1,774 @@ +/* Copyright (C) 2010-2020 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (disk_control_interface.c). + * --------------------------------------------------------------------------------------- + * + * Permission is hereby granted, free of charge, + * to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include + +#include "paths.h" +#include "retroarch.h" +#include "verbosity.h" +#include "msg_hash.h" + +#include "disk_control_interface.h" + +/*****************/ +/* Configuration */ +/*****************/ + +/* Sets all disk interface callback functions + * to NULL */ +static void disk_control_reset_callback( + disk_control_interface_t *disk_control) +{ + if (!disk_control) + return; + + memset(&disk_control->cb, 0, sizeof(struct retro_disk_control_ext_callback)); +} + +/* Set v0 disk interface callback functions */ +void disk_control_set_callback( + disk_control_interface_t *disk_control, + const struct retro_disk_control_callback *cb) +{ + if (!disk_control) + return; + + disk_control_reset_callback(disk_control); + + if (!cb) + return; + + disk_control->cb.set_eject_state = cb->set_eject_state; + disk_control->cb.get_eject_state = cb->get_eject_state; + disk_control->cb.get_image_index = cb->get_image_index; + disk_control->cb.set_image_index = cb->set_image_index; + disk_control->cb.get_num_images = cb->get_num_images; + disk_control->cb.replace_image_index = cb->replace_image_index; + disk_control->cb.add_image_index = cb->add_image_index; +} + +/* Set v1+ disk interface callback functions */ +void disk_control_set_ext_callback( + disk_control_interface_t *disk_control, + const struct retro_disk_control_ext_callback *cb) +{ + if (!disk_control) + return; + + disk_control_reset_callback(disk_control); + + if (!cb) + return; + + disk_control->cb.set_eject_state = cb->set_eject_state; + disk_control->cb.get_eject_state = cb->get_eject_state; + disk_control->cb.get_image_index = cb->get_image_index; + disk_control->cb.set_image_index = cb->set_image_index; + disk_control->cb.get_num_images = cb->get_num_images; + disk_control->cb.replace_image_index = cb->replace_image_index; + disk_control->cb.add_image_index = cb->add_image_index; + + disk_control->cb.set_initial_image = cb->set_initial_image; + disk_control->cb.get_image_path = cb->get_image_path; + disk_control->cb.get_image_label = cb->get_image_label; +} + +/**********/ +/* Status */ +/**********/ + +/* Returns true if core supports basic disk + * control functionality + * - set_eject_state + * - get_eject_state + * - get_image_index + * - set_image_index + * - get_num_images */ +bool disk_control_enabled( + disk_control_interface_t *disk_control) +{ + if (!disk_control) + return false; + + if (disk_control->cb.set_eject_state && + disk_control->cb.get_eject_state && + disk_control->cb.get_image_index && + disk_control->cb.set_image_index && + disk_control->cb.get_num_images) + return true; + + return false; +} + +/* Returns true if core supports disk append + * functionality + * - replace_image_index + * - add_image_index */ +bool disk_control_append_enabled( + disk_control_interface_t *disk_control) +{ + if (!disk_control) + return false; + + if (disk_control->cb.replace_image_index && + disk_control->cb.add_image_index) + return true; + + return false; +} + +/* Returns true if core supports image + * labels + * - get_image_label */ +bool disk_control_image_label_enabled( + disk_control_interface_t *disk_control) +{ + if (!disk_control) + return false; + + if (disk_control->cb.get_image_label) + return true; + + return false; +} + +/* Returns true if core supports setting + * initial disk index + * - set_initial_image + * - get_image_path */ +bool disk_control_initial_image_enabled( + disk_control_interface_t *disk_control) +{ + if (!disk_control) + return false; + + if (disk_control->cb.set_initial_image && + disk_control->cb.get_image_path) + return true; + + return false; +} + +/***********/ +/* Getters */ +/***********/ + +/* Returns true if disk is currently ejected */ +bool disk_control_get_eject_state( + disk_control_interface_t *disk_control) +{ + if (!disk_control) + return false; + + if (!disk_control->cb.get_eject_state) + return false; + + return disk_control->cb.get_eject_state(); +} + +/* Returns number of disk images registered + * by the core */ +unsigned disk_control_get_num_images( + disk_control_interface_t *disk_control) +{ + if (!disk_control) + return 0; + + if (!disk_control->cb.get_num_images) + return 0; + + return disk_control->cb.get_num_images(); +} + +/* Returns currently selected disk image index */ +unsigned disk_control_get_image_index( + disk_control_interface_t *disk_control) +{ + if (!disk_control) + return 0; + + if (!disk_control->cb.get_image_index) + return 0; + + return disk_control->cb.get_image_index(); +} + +/* Fetches core-provided disk image label + * (label is set to an empty string if core + * does not support image labels) */ +void disk_control_get_image_label( + disk_control_interface_t *disk_control, + unsigned index, char *label, size_t len) +{ + if (!label || len < 1) + return; + + if (!disk_control) + goto error; + + if (!disk_control->cb.get_image_label) + goto error; + + if (!disk_control->cb.get_image_label(index, label, len)) + goto error; + + return; + +error: + label[0] = '\0'; + return; +} + +/***********/ +/* Setters */ +/***********/ + +/* Sets the eject state of the virtual disk tray */ +bool disk_control_set_eject_state( + disk_control_interface_t *disk_control, + bool eject, bool verbose) +{ + bool error = false; + char msg[128]; + + msg[0] = '\0'; + + if (!disk_control) + return false; + + if (!disk_control->cb.set_eject_state) + return false; + + /* Set eject state */ + if (disk_control->cb.set_eject_state(eject)) + snprintf( + msg, sizeof(msg), "%s %s", + eject ? msg_hash_to_str(MSG_DISK_EJECTED) : + msg_hash_to_str(MSG_DISK_CLOSED), + msg_hash_to_str(MSG_VIRTUAL_DISK_TRAY)); + else + { + error = true; + snprintf( + msg, sizeof(msg), "%s %s %s", + msg_hash_to_str(MSG_FAILED_TO), + eject ? msg_hash_to_str(MSG_VIRTUAL_DISK_TRAY_EJECT) : + msg_hash_to_str(MSG_VIRTUAL_DISK_TRAY_CLOSE), + msg_hash_to_str(MSG_VIRTUAL_DISK_TRAY)); + } + + if (!string_is_empty(msg)) + { + if (error) + RARCH_ERR("%s\n", msg); + else + RARCH_LOG("%s\n", msg); + + /* Errors should always be displayed */ + if (verbose || error) + runloop_msg_queue_push( + msg, 1, error ? 180 : 60, + true, NULL, + MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); + } + + return !error; +} + +/* Sets currently selected disk index + * NOTE: Will fail if disk is not currently ejected */ +bool disk_control_set_index( + disk_control_interface_t *disk_control, + unsigned index, bool verbose) +{ + bool error = false; + unsigned num_images = 0; + char msg[128]; + + msg[0] = '\0'; + + if (!disk_control) + return false; + + if (!disk_control->cb.get_eject_state || + !disk_control->cb.get_num_images || + !disk_control->cb.set_image_index) + return false; + + /* Ensure that disk is currently ejected */ + if (!disk_control->cb.get_eject_state()) + return false; + + num_images = disk_control->cb.get_num_images(); + + if (disk_control->cb.set_image_index(index)) + { + if (index < num_images) + snprintf( + msg, sizeof(msg), "%s: %u/%u", + msg_hash_to_str(MSG_SETTING_DISK_IN_TRAY), + index + 1, num_images); + else + strlcpy( + msg, + msg_hash_to_str(MSG_REMOVED_DISK_FROM_TRAY), + sizeof(msg)); + } + else + { + error = true; + + if (index < num_images) + snprintf( + msg, sizeof(msg), "%s %u/%u", + msg_hash_to_str(MSG_FAILED_TO_SET_DISK), + index + 1, num_images); + else + strlcpy( + msg, + msg_hash_to_str(MSG_FAILED_TO_REMOVE_DISK_FROM_TRAY), + sizeof(msg)); + } + + if (!string_is_empty(msg)) + { + if (error) + RARCH_ERR("%s\n", msg); + else + RARCH_LOG("%s\n", msg); + + /* Errors should always be displayed */ + if (verbose || error) + runloop_msg_queue_push( + msg, 1, error ? 180 : 60, + true, NULL, + MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); + } + + /* If operation was successful, update disk + * index record (if enabled) */ + if (!error && disk_control->record_enabled) + { + if (disk_control->cb.get_image_index && + disk_control->cb.get_image_path) + { + bool image_path_valid = false; + unsigned new_image_index = 0; + char new_image_path[PATH_MAX_LENGTH]; + + new_image_path[0] = '\0'; + + /* Get current image index + path */ + new_image_index = disk_control->cb.get_image_index(); + image_path_valid = disk_control->cb.get_image_path( + new_image_index, new_image_path, sizeof(new_image_path)); + + if (image_path_valid) + disk_index_file_set( + &disk_control->index_record, + new_image_index, new_image_path); + else + disk_index_file_set( + &disk_control->index_record, 0, NULL); + } + } + + return !error; +} + +/* Increments selected disk index */ +bool disk_control_set_index_next( + disk_control_interface_t *disk_control, + bool verbose) +{ + unsigned num_images = 0; + unsigned image_index = 0; + bool disk_next_enable = false; + + if (!disk_control) + return false; + + if (!disk_control->cb.get_num_images || + !disk_control->cb.get_image_index) + return false; + + num_images = disk_control->cb.get_num_images(); + image_index = disk_control->cb.get_image_index(); + /* Would seem more sensible to check (num_images > 1) + * here, but seems we need to be able to cycle the + * same image for legacy reasons... */ + disk_next_enable = (num_images > 0) && (num_images != UINT_MAX); + + if (!disk_next_enable) + { + RARCH_ERR("%s.\n", msg_hash_to_str(MSG_GOT_INVALID_DISK_INDEX)); + return false; + } + + if (image_index < (num_images - 1)) + image_index++; + + return disk_control_set_index(disk_control, image_index, verbose); +} + +/* Decrements selected disk index */ +bool disk_control_set_index_prev( + disk_control_interface_t *disk_control, + bool verbose) +{ + unsigned num_images = 0; + unsigned image_index = 0; + bool disk_prev_enable = false; + + if (!disk_control) + return false; + + if (!disk_control->cb.get_num_images || + !disk_control->cb.get_image_index) + return false; + + num_images = disk_control->cb.get_num_images(); + image_index = disk_control->cb.get_image_index(); + /* Would seem more sensible to check (num_images > 1) + * here, but seems we need to be able to cycle the + * same image for legacy reasons... */ + disk_prev_enable = (num_images > 0); + + if (!disk_prev_enable) + { + RARCH_ERR("%s.\n", msg_hash_to_str(MSG_GOT_INVALID_DISK_INDEX)); + return false; + } + + if (image_index > 0) + image_index--; + + return disk_control_set_index(disk_control, image_index, verbose); +} + +/* Appends specified image file to disk image list */ +bool disk_control_append_image( + disk_control_interface_t *disk_control, + const char *image_path) +{ + unsigned initial_index = 0; + unsigned new_index = 0; + const char *image_filename = NULL; + struct retro_game_info info = {0}; + char msg[128]; + + msg[0] = '\0'; + + /* Sanity check. If any of these fail then a + * frontend error has occurred - we will not + * deal with that here */ + if (!disk_control) + return false; + + if (!disk_control->cb.get_image_index || + !disk_control->cb.get_num_images || + !disk_control->cb.add_image_index || + !disk_control->cb.replace_image_index || + !disk_control->cb.get_eject_state) + return false; + + if (string_is_empty(image_path)) + return false; + + image_filename = path_basename(image_path); + + if (string_is_empty(image_filename)) + return false; + + /* Cache initial image index */ + initial_index = disk_control->cb.get_image_index(); + + /* Eject disk */ + if (!disk_control_set_eject_state(disk_control, true, false)) + goto error; + + /* Append image */ + if (!disk_control->cb.add_image_index()) + goto error; + + new_index = disk_control->cb.get_num_images(); + if (new_index < 1) + goto error; + new_index--; + + info.path = image_path; + if (!disk_control->cb.replace_image_index(new_index, &info)) + goto error; + + /* Set new index */ + if (!disk_control_set_index(disk_control, new_index, false)) + goto error; + + /* Insert disk */ + if (!disk_control_set_eject_state(disk_control, false, false)) + goto error; + + /* Display log */ + snprintf( + msg, sizeof(msg), "%s: %s", + msg_hash_to_str(MSG_APPENDED_DISK), image_filename); + + RARCH_LOG("%s\n", msg); + /* This message should always be displayed, since + * the menu itself does not provide sufficient + * visual feedback */ + runloop_msg_queue_push( + msg, 0, 120, + true, NULL, + MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); + + return true; + +error: + /* If we reach this point then everything is + * broken and the disk control interface is + * in an undefined state. Try to restore some + * sanity by reinserting the original disk... + * NOTE: If this fails then it's game over - + * just display the error notification and + * hope for the best... */ + if (!disk_control->cb.get_eject_state()) + disk_control_set_eject_state(disk_control, true, false); + disk_control_set_index(disk_control, initial_index, false); + disk_control_set_eject_state(disk_control, false, false); + + snprintf( + msg, sizeof(msg), "%s: %s", + msg_hash_to_str(MSG_FAILED_TO_APPEND_DISK), image_filename); + + runloop_msg_queue_push( + msg, 0, 180, + true, NULL, + MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); + + return false; +} + +/*****************************/ +/* 'Initial index' functions */ +/*****************************/ + +/* Attempts to set current core's initial disk index. + * > disk_control->record_enabled will be set to + * 'false' if core does not support initial + * index functionality + * > disk_control->index_record will be loaded + * from file (if an existing record is found) + * NOTE: Must be called immediately before + * loading content */ +bool disk_control_set_initial_index( + disk_control_interface_t *disk_control, + const char *content_path, + const char *dir_savefile) +{ + if (!disk_control) + return false; + + if (string_is_empty(content_path)) + goto error; + + /* Check that 'initial index' functionality is enabled */ + if (!disk_control->cb.set_initial_image || + !disk_control->cb.get_num_images || + !disk_control->cb.get_image_index || + !disk_control->cb.get_image_path) + goto error; + + /* Attempt to initialise disk index record (reading + * from disk, if file exists) */ + disk_control->record_enabled = disk_index_file_init( + &disk_control->index_record, + content_path, dir_savefile); + + /* If record is enabled and initial index is *not* + * zero, notify current core */ + if (disk_control->record_enabled && + (disk_control->index_record.image_index != 0)) + { + if (!disk_control->cb.set_initial_image( + disk_control->index_record.image_index, + disk_control->index_record.image_path)) + { + /* Note: We don't bother with an on-screen + * notification at this stage, since an error + * here may not matter (have to wait until + * disk index is verified) */ + RARCH_ERR( + "Failed to set initial disk index: [%u] %s\n", + disk_control->index_record.image_index, + disk_control->index_record.image_path); + return false; + } + } + + return true; + +error: + disk_control->record_enabled = false; + return false; +} + +/* Checks that initial index has been set correctly + * and provides user notification. + * > Sets disk_control->initial_num_images if + * if functionality is supported by core + * NOTE: Must be called immediately after + * loading content */ +bool disk_control_verify_initial_index(disk_control_interface_t *disk_control) +{ + bool success = false; + unsigned image_index = 0; + char image_path[PATH_MAX_LENGTH]; + + image_path[0] = '\0'; + + if (!disk_control) + return false; + + /* If index record is disabled, can return immediately */ + if (!disk_control->record_enabled) + return false; + + /* Check that 'initial index' functionality is enabled */ + if (!disk_control->cb.set_initial_image || + !disk_control->cb.get_num_images || + !disk_control->cb.get_image_index || + !disk_control->cb.get_image_path) + return false; + + /* Cache initial number of images + * (required for error checking when saving + * disk index file) */ + disk_control->initial_num_images = + disk_control->cb.get_num_images(); + + /* Get current image index + path */ + image_index = disk_control->cb.get_image_index(); + + if (disk_control->cb.get_image_path( + image_index, image_path, sizeof(image_path))) + { + /* Check whether index + path match set + * values + * > Note that if set index was zero and + * set path was empty, we ignore the path + * read here (since this corresponds to a + * 'first run', where no existing disk index + * file was present) */ + if ((image_index == disk_control->index_record.image_index) && + (string_is_equal(image_path, disk_control->index_record.image_path) || + ((disk_control->index_record.image_index == 0) && + string_is_empty(disk_control->index_record.image_path)))) + success = true; + } + + /* If current disk is incorrect, notify user */ + if (!success) + { + RARCH_ERR( + "Failed to set initial disk index:\n> Expected [%u] %s\n> Detected [%u] %s\n", + disk_control->index_record.image_index + 1, + disk_control->index_record.image_path, + image_index + 1, + image_path); + + runloop_msg_queue_push( + msg_hash_to_str(MSG_FAILED_TO_SET_INITIAL_DISK), + 0, 60, + true, NULL, + MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); + + /* Since a failure here typically means that the + * original M3U content file has been altered, + * any existing disk index record file will be + * invalid. We therefore 'reset' and save the disk + * index record to prevent a repeat of the error on + * the next run */ + disk_index_file_set(&disk_control->index_record, 0, NULL); + disk_index_file_save(&disk_control->index_record); + } + /* If current disk is correct and recorded image + * path is empty (i.e. first run), need to register + * current image path */ + else if (string_is_empty(disk_control->index_record.image_path)) + disk_index_file_set( + &disk_control->index_record, image_index, image_path); + + /* Regardless of success/failure, notify user of + * current disk index *if* more than one disk + * is available */ + if (disk_control->initial_num_images > 1) + { + char msg[128]; + + msg[0] = '\0'; + + snprintf( + msg, sizeof(msg), "%s: %u/%u", + msg_hash_to_str(MSG_SETTING_DISK_IN_TRAY), + image_index + 1, disk_control->initial_num_images); + + RARCH_LOG("%s\n", msg); + + runloop_msg_queue_push( + msg, + 0, 60, + true, NULL, + MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); + } + + return success; +} + +/* Saves current disk index to file, if supported + * by current core */ +bool disk_control_save_image_index(disk_control_interface_t *disk_control) +{ + if (!disk_control) + return false; + + /* If index record is disabled, can return immediately */ + if (!disk_control->record_enabled) + return false; + + /* If core started with less than two disks, + * then a disk index record is unnecessary */ + if (disk_control->initial_num_images < 2) + return false; + + /* If current index is greater than initial + * number of disks then user has appended a + * disk and it is currently active. This setup + * *cannot* be restored, so cancel the file save */ + if (disk_control->index_record.image_index >= + disk_control->initial_num_images) + return false; + + /* Save record */ + return disk_index_file_save(&disk_control->index_record); +} diff --git a/disk_control_interface.h b/disk_control_interface.h new file mode 100644 index 0000000000..b705838271 --- /dev/null +++ b/disk_control_interface.h @@ -0,0 +1,178 @@ +/* Copyright (C) 2010-2020 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (disk_control_interface.h). + * --------------------------------------------------------------------------------------- + * + * Permission is hereby granted, free of charge, + * to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __DISK_CONTROL_INTERFACE_H +#define __DISK_CONTROL_INTERFACE_H + +#include +#include + +#include + +#include "disk_index_file.h" + +RETRO_BEGIN_DECLS + +/* Holds all all objects to operate the disk + * control interface */ +typedef struct +{ + bool record_enabled; + disk_index_file_t index_record; + unsigned initial_num_images; + struct retro_disk_control_ext_callback cb; +} disk_control_interface_t; + +/*****************/ +/* Configuration */ +/*****************/ + +/* Set v0 disk interface callback functions */ +void disk_control_set_callback( + disk_control_interface_t *disk_control, + const struct retro_disk_control_callback *cb); + +/* Set v1+ disk interface callback functions */ +void disk_control_set_ext_callback( + disk_control_interface_t *disk_control, + const struct retro_disk_control_ext_callback *cb); + +/**********/ +/* Status */ +/**********/ + +/* Returns true if core supports basic disk + * control functionality + * - set_eject_state + * - get_eject_state + * - get_image_index + * - set_image_index + * - get_num_images */ +bool disk_control_enabled( + disk_control_interface_t *disk_control); + +/* Returns true if core supports disk append + * functionality + * - replace_image_index + * - add_image_index */ +bool disk_control_append_enabled( + disk_control_interface_t *disk_control); + +/* Returns true if core supports image + * labels + * - get_image_label */ +bool disk_control_image_label_enabled( + disk_control_interface_t *disk_control); + +/* Returns true if core supports setting + * initial disk index + * - set_initial_image + * - get_image_path */ +bool disk_control_initial_image_enabled( + disk_control_interface_t *disk_control); + +/***********/ +/* Getters */ +/***********/ + +/* Returns true if disk is currently ejected */ +bool disk_control_get_eject_state( + disk_control_interface_t *disk_control); + +/* Returns number of disk images registered + * by the core */ +unsigned disk_control_get_num_images( + disk_control_interface_t *disk_control); + +/* Returns currently selected disk image index */ +unsigned disk_control_get_image_index( + disk_control_interface_t *disk_control); + +/* Fetches core-provided disk image label + * (label is set to an empty string if core + * does not support image labels) */ +void disk_control_get_image_label( + disk_control_interface_t *disk_control, + unsigned index, char *label, size_t len); + +/***********/ +/* Setters */ +/***********/ + +/* Sets the eject state of the virtual disk tray */ +bool disk_control_set_eject_state( + disk_control_interface_t *disk_control, + bool eject, bool verbose); + +/* Sets currently selected disk index + * NOTE: Will fail if disk is not currently ejected */ +bool disk_control_set_index( + disk_control_interface_t *disk_control, + unsigned index, bool verbose); + +/* Increments selected disk index */ +bool disk_control_set_index_next( + disk_control_interface_t *disk_control, + bool verbose); + +/* Decrements selected disk index */ +bool disk_control_set_index_prev( + disk_control_interface_t *disk_control, + bool verbose); + +/* Appends specified image file to disk image list */ +bool disk_control_append_image( + disk_control_interface_t *disk_control, + const char *image_path); + +/*****************************/ +/* 'Initial index' functions */ +/*****************************/ + +/* Attempts to set current core's initial disk index. + * > disk_control->record_enabled will be set to + * 'false' if core does not support initial + * index functionality + * > disk_control->index_record will be loaded + * from file (if an existing record is found) + * NOTE: Must be called immediately before + * loading content */ +bool disk_control_set_initial_index( + disk_control_interface_t *disk_control, + const char *content_path, + const char *dir_savefile); + +/* Checks that initial index has been set correctly + * and provides user notification. + * > Sets disk_control->initial_num_images if + * if functionality is supported by core + * NOTE: Must be called immediately after + * loading content */ +bool disk_control_verify_initial_index(disk_control_interface_t *disk_control); + +/* Saves current disk index to file, if supported + * by current core */ +bool disk_control_save_image_index(disk_control_interface_t *disk_control); + +RETRO_END_DECLS + +#endif diff --git a/disk_index_file.c b/disk_index_file.c new file mode 100644 index 0000000000..6655336ec5 --- /dev/null +++ b/disk_index_file.c @@ -0,0 +1,523 @@ +/* Copyright (C) 2010-2020 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (disk_index_file.c). + * --------------------------------------------------------------------------------------- + * + * Permission is hereby granted, free of charge, + * to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include + +#include "file_path_special.h" +#include "verbosity.h" +#include "msg_hash.h" + +#include "disk_index_file.h" + +/****************/ +/* JSON Helpers */ +/****************/ + +typedef struct +{ + JSON_Parser parser; + JSON_Writer writer; + RFILE *file; + unsigned *current_entry_uint_val; + char **current_entry_str_val; + unsigned image_index; + char *image_path; +} DCifJSONContext; + +static JSON_Parser_HandlerResult DCifJSONObjectMemberHandler(JSON_Parser parser, char *pValue, size_t length, JSON_StringAttributes attributes) +{ + DCifJSONContext *pCtx = (DCifJSONContext*)JSON_Parser_GetUserData(parser); + (void)attributes; /* unused */ + + if (pCtx->current_entry_str_val) + { + /* something went wrong */ + RARCH_ERR("[disk index file] JSON parsing failed at line %d.\n", __LINE__); + return JSON_Parser_Abort; + } + + if (length) + { + if (string_is_equal(pValue, "image_index")) + pCtx->current_entry_uint_val = &pCtx->image_index; + else if (string_is_equal(pValue, "image_path")) + pCtx->current_entry_str_val = &pCtx->image_path; + /* ignore unknown members */ + } + + return JSON_Parser_Continue; +} + +static JSON_Parser_HandlerResult DCifJSONNumberHandler(JSON_Parser parser, char *pValue, size_t length, JSON_StringAttributes attributes) +{ + DCifJSONContext *pCtx = (DCifJSONContext*)JSON_Parser_GetUserData(parser); + (void)attributes; /* unused */ + + if (pCtx->current_entry_uint_val && length && !string_is_empty(pValue)) + *pCtx->current_entry_uint_val = string_to_unsigned(pValue); + /* ignore unknown members */ + + pCtx->current_entry_uint_val = NULL; + + return JSON_Parser_Continue; +} + +static JSON_Parser_HandlerResult DCifJSONStringHandler(JSON_Parser parser, char *pValue, size_t length, JSON_StringAttributes attributes) +{ + DCifJSONContext *pCtx = (DCifJSONContext*)JSON_Parser_GetUserData(parser); + (void)attributes; /* unused */ + + if (pCtx->current_entry_str_val && length && !string_is_empty(pValue)) + { + if (*pCtx->current_entry_str_val) + free(*pCtx->current_entry_str_val); + + *pCtx->current_entry_str_val = strdup(pValue); + } + /* ignore unknown members */ + + pCtx->current_entry_str_val = NULL; + + return JSON_Parser_Continue; +} + +static JSON_Writer_HandlerResult DCifJSONOutputHandler(JSON_Writer writer, const char *pBytes, size_t length) +{ + DCifJSONContext *context = (DCifJSONContext*)JSON_Writer_GetUserData(writer); + (void)writer; /* unused */ + + return filestream_write(context->file, pBytes, length) == length ? JSON_Writer_Continue : JSON_Writer_Abort; +} + +static void DCifJSONLogError(DCifJSONContext *pCtx) +{ + if (pCtx->parser && JSON_Parser_GetError(pCtx->parser) != JSON_Error_AbortedByHandler) + { + JSON_Error error = JSON_Parser_GetError(pCtx->parser); + JSON_Location errorLocation = { 0, 0, 0 }; + + (void)JSON_Parser_GetErrorLocation(pCtx->parser, &errorLocation); + RARCH_ERR("[disk index file] Error: Invalid JSON at line %d, column %d (input byte %d) - %s.\n", + (int)errorLocation.line + 1, + (int)errorLocation.column + 1, + (int)errorLocation.byte, + JSON_ErrorString(error)); + } + else if (pCtx->writer && JSON_Writer_GetError(pCtx->writer) != JSON_Error_AbortedByHandler) + { + RARCH_ERR("[disk index file] Error: could not write output - %s.\n", JSON_ErrorString(JSON_Writer_GetError(pCtx->writer))); + } +} + +/******************/ +/* Initialisation */ +/******************/ + +/* Resets existing disk index record */ +static void disk_index_file_reset(disk_index_file_t *disk_index_file) +{ + if (!disk_index_file) + return; + + disk_index_file->modified = false; + disk_index_file->image_index = 0; + disk_index_file->image_path[0] = '\0'; + disk_index_file->file_path[0] = '\0'; +} + +/* Parses disk index file referenced by + * disk_index_file->file_path. + * Does nothing if disk index file does not exist. */ +static bool disk_index_file_read(disk_index_file_t *disk_index_file) +{ + bool success = false; + DCifJSONContext context = {0}; + RFILE *file = NULL; + + /* Sanity check */ + if (!disk_index_file) + return false; + + if (string_is_empty(disk_index_file->file_path)) + return false; + + if (!path_is_valid(disk_index_file->file_path)) + return false; + + /* Attempt to open disk index file */ + file = filestream_open( + disk_index_file->file_path, + RETRO_VFS_FILE_ACCESS_READ, + RETRO_VFS_FILE_ACCESS_HINT_NONE); + + if (!file) + { + RARCH_ERR( + "[disk index file] Failed to open disk index record file: %s\n", + disk_index_file->file_path); + return false; + } + + /* Initialise JSON parser */ + context.image_index = 0; + context.image_path = NULL; + context.parser = JSON_Parser_Create(NULL); + context.file = file; + + if (!context.parser) + { + RARCH_ERR("[disk index file] Failed to create JSON parser.\n"); + goto end; + } + + /* Configure parser */ + JSON_Parser_SetAllowBOM(context.parser, JSON_True); + JSON_Parser_SetNumberHandler(context.parser, &DCifJSONNumberHandler); + JSON_Parser_SetStringHandler(context.parser, &DCifJSONStringHandler); + JSON_Parser_SetObjectMemberHandler(context.parser, &DCifJSONObjectMemberHandler); + JSON_Parser_SetUserData(context.parser, &context); + + /* Read file */ + while (!filestream_eof(file)) + { + /* Disk index files are tiny - use small chunk size */ + char chunk[128] = {0}; + int64_t length = filestream_read(file, chunk, sizeof(chunk)); + + /* Error checking... */ + if (!length && !filestream_eof(file)) + { + RARCH_ERR( + "[disk index file] Failed to read disk index file: %s\n", + disk_index_file->file_path); + JSON_Parser_Free(context.parser); + goto end; + } + + /* Parse chunk */ + if (!JSON_Parser_Parse(context.parser, chunk, length, JSON_False)) + { + RARCH_ERR( + "[disk index file] Error parsing chunk of disk index file: %s\n---snip---\n%s\n---snip---\n", + disk_index_file->file_path, chunk); + DCifJSONLogError(&context); + JSON_Parser_Free(context.parser); + goto end; + } + } + + /* Finalise parsing */ + if (!JSON_Parser_Parse(context.parser, NULL, 0, JSON_True)) + { + RARCH_WARN( + "[disk index file] Error parsing disk index file: %s\n", + disk_index_file->file_path); + DCifJSONLogError(&context); + JSON_Parser_Free(context.parser); + goto end; + } + + /* Free parser */ + JSON_Parser_Free(context.parser); + + /* Copy values read from JSON file */ + disk_index_file->image_index = context.image_index; + + if (!string_is_empty(context.image_path)) + strlcpy( + disk_index_file->image_path, context.image_path, + sizeof(disk_index_file->image_path)); + else + disk_index_file->image_path[0] = '\0'; + + success = true; + +end: + /* Clean up leftover strings */ + if (context.image_path) + free(context.image_path); + + /* Close log file */ + filestream_close(file); + + return success; +} + +/* Initialises existing disk index record, loading + * current parameters if a record file exists. + * Returns false if arguments are invalid. */ +bool disk_index_file_init( + disk_index_file_t *disk_index_file, + const char *content_path, + const char *dir_savefile) +{ + const char *content_file = NULL; + char content_name[PATH_MAX_LENGTH]; + char disk_index_file_dir[PATH_MAX_LENGTH]; + char disk_index_file_path[PATH_MAX_LENGTH]; + + content_name[0] = '\0'; + disk_index_file_dir[0] = '\0'; + disk_index_file_path[0] = '\0'; + + /* Sanity check */ + if (!disk_index_file) + return false; + + /* Disk index records are only valid when loading + * content (i.e. they do not apply to contentless + * cores) */ + if (string_is_empty(content_path)) + goto error; + + /* Build disk index file path */ + + /* > Get content name */ + content_file = path_basename(content_path); + if (string_is_empty(content_file)) + goto error; + + strlcpy(content_name, content_file, sizeof(content_name)); + path_remove_extension(content_name); + if (string_is_empty(content_name)) + goto error; + + /* > Get disk index file directory */ + if (!string_is_empty(dir_savefile)) + strlcpy(disk_index_file_dir, dir_savefile, sizeof(disk_index_file_dir)); + else + { + /* Use content directory */ + strlcpy(disk_index_file_dir, content_path, sizeof(disk_index_file_dir)); + path_basedir(disk_index_file_dir); + } + + /* > Create directory, if required */ + if (!path_is_directory(disk_index_file_dir)) + { + if (!path_mkdir(disk_index_file_dir)) + { + RARCH_ERR( + "[disk index file] failed to create directory for disk index file: %s\n", + disk_index_file_dir); + goto error; + } + } + + /* > Generate final path */ + fill_pathname_join( + disk_index_file_path, disk_index_file_dir, + content_name, sizeof(disk_index_file_path)); + strlcat( + disk_index_file_path, + file_path_str(FILE_PATH_DISK_CONTROL_INDEX_EXTENSION), + sizeof(disk_index_file_path)); + if (string_is_empty(disk_index_file_path)) + goto error; + + /* All is well - reset disk_index_file_t and + * attempt to load values from file */ + disk_index_file_reset(disk_index_file); + strlcpy( + disk_index_file->file_path, + disk_index_file_path, + sizeof(disk_index_file->file_path)); + + /* > If file does not exist (or some other + * error occurs) then this is a new record + * - in this case, 'modified' flag should + * be set to 'true' */ + if (!disk_index_file_read(disk_index_file)) + disk_index_file->modified = true; + + return true; + +error: + disk_index_file_reset(disk_index_file); + return false; +} + +/***********/ +/* Setters */ +/***********/ + +/* Sets image index and path */ +void disk_index_file_set( + disk_index_file_t *disk_index_file, + unsigned image_index, + const char *image_path) +{ + if (!disk_index_file) + return; + + /* Check whether image index should be updated */ + if (disk_index_file->image_index != image_index) + { + disk_index_file->image_index = image_index; + disk_index_file->modified = true; + } + + /* Check whether image path should be updated */ + if (!string_is_empty(image_path)) + { + if (!string_is_equal(disk_index_file->image_path, image_path)) + { + strlcpy( + disk_index_file->image_path, image_path, + sizeof(disk_index_file->image_path)); + disk_index_file->modified = true; + } + } + else if (!string_is_empty(disk_index_file->image_path)) + { + disk_index_file->image_path[0] = '\0'; + disk_index_file->modified = true; + } +} + +/**********/ +/* Saving */ +/**********/ + +/* Saves specified disk index file to disk */ +bool disk_index_file_save(disk_index_file_t *disk_index_file) +{ + char value_string[32]; + DCifJSONContext context = {0}; + RFILE *file = NULL; + bool success = false; + int n; + + value_string[0] = '\0'; + + /* Sanity check */ + if (!disk_index_file) + return false; + + /* > Only save file if record has been modified. + * We return true in this case - since there + * was nothing to write, there can be no + * 'failure' */ + if (!disk_index_file->modified) + return true; + + if (string_is_empty(disk_index_file->file_path)) + return false; + + RARCH_LOG( + "[disk index file] Saving disk index file: %s\n", + disk_index_file->file_path); + + /* Attempt to open disk index file */ + file = filestream_open( + disk_index_file->file_path, + RETRO_VFS_FILE_ACCESS_WRITE, + RETRO_VFS_FILE_ACCESS_HINT_NONE); + + if (!file) + { + RARCH_ERR( + "[disk index file] Failed to open disk index file: %s\n", + disk_index_file->file_path); + return false; + } + + /* Initialise JSON writer */ + context.writer = JSON_Writer_Create(NULL); + context.file = file; + + if (!context.writer) + { + RARCH_ERR("[disk index file] Failed to create JSON writer.\n"); + goto end; + } + + /* Configure JSON writer */ + JSON_Writer_SetOutputEncoding(context.writer, JSON_UTF8); + JSON_Writer_SetOutputHandler(context.writer, &DCifJSONOutputHandler); + JSON_Writer_SetUserData(context.writer, &context); + + /* Write output file */ + JSON_Writer_WriteStartObject(context.writer); + JSON_Writer_WriteNewLine(context.writer); + + /* > Version entry */ + JSON_Writer_WriteSpace(context.writer, 2); + JSON_Writer_WriteString(context.writer, "version", + STRLEN_CONST("version"), JSON_UTF8); + JSON_Writer_WriteColon(context.writer); + JSON_Writer_WriteSpace(context.writer, 1); + JSON_Writer_WriteString(context.writer, "1.0", + STRLEN_CONST("1.0"), JSON_UTF8); + JSON_Writer_WriteComma(context.writer); + JSON_Writer_WriteNewLine(context.writer); + + /* > image index entry */ + n = snprintf( + value_string, sizeof(value_string), + "%u", disk_index_file->image_index); + if ((n < 0) || (n >= 32)) + n = 0; /* Silence GCC warnings... */ + + JSON_Writer_WriteSpace(context.writer, 2); + JSON_Writer_WriteString(context.writer, "image_index", + STRLEN_CONST("image_index"), JSON_UTF8); + JSON_Writer_WriteColon(context.writer); + JSON_Writer_WriteSpace(context.writer, 1); + JSON_Writer_WriteNumber(context.writer, value_string, + strlen(value_string), JSON_UTF8); + JSON_Writer_WriteComma(context.writer); + JSON_Writer_WriteNewLine(context.writer); + + /* > image path entry */ + JSON_Writer_WriteSpace(context.writer, 2); + JSON_Writer_WriteString(context.writer, "image_path", + STRLEN_CONST("image_path"), JSON_UTF8); + JSON_Writer_WriteColon(context.writer); + JSON_Writer_WriteSpace(context.writer, 1); + JSON_Writer_WriteString(context.writer, + disk_index_file->image_path, + strlen(disk_index_file->image_path), JSON_UTF8); + JSON_Writer_WriteNewLine(context.writer); + + /* > Finalise */ + JSON_Writer_WriteEndObject(context.writer); + JSON_Writer_WriteNewLine(context.writer); + + /* Free JSON writer */ + JSON_Writer_Free(context.writer); + + /* Changes have been written - record + * is no longer considered to be in a + * 'modified' state */ + disk_index_file->modified = false; + success = true; + +end: + /* Close disk index file */ + filestream_close(file); + + return success; +} diff --git a/disk_index_file.h b/disk_index_file.h new file mode 100644 index 0000000000..109bc82c12 --- /dev/null +++ b/disk_index_file.h @@ -0,0 +1,76 @@ +/* Copyright (C) 2010-2020 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (disk_index_file.h). + * --------------------------------------------------------------------------------------- + * + * Permission is hereby granted, free of charge, + * to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __DISK_INDEX_FILE_H +#define __DISK_INDEX_FILE_H + +#include +#include +#include + +#include + +RETRO_BEGIN_DECLS + +/* Holds all parameters required for recording + * the last disk image selected via the disk + * control interface */ +typedef struct +{ + bool modified; + unsigned image_index; + char image_path[PATH_MAX_LENGTH]; + char file_path[PATH_MAX_LENGTH]; +} disk_index_file_t; + +/******************/ +/* Initialisation */ +/******************/ + +/* Initialises existing disk index record, loading + * current parameters if a record file exists. + * Returns false if arguments are invalid. */ +bool disk_index_file_init( + disk_index_file_t *disk_index_file, + const char *content_path, + const char *dir_savefile); + +/***********/ +/* Setters */ +/***********/ + +/* Sets image index and path */ +void disk_index_file_set( + disk_index_file_t *disk_index_file, + unsigned image_index, + const char *image_path); + +/**********/ +/* Saving */ +/**********/ + +/* Saves specified disk index file to disk */ +bool disk_index_file_save(disk_index_file_t *disk_index_file); + +RETRO_END_DECLS + +#endif diff --git a/file_path_special.h b/file_path_special.h index a6b13d2671..12e85bb806 100644 --- a/file_path_special.h +++ b/file_path_special.h @@ -95,7 +95,8 @@ enum file_path_enum FILE_PATH_CORE_INFO_EXTENSION, FILE_PATH_RUNTIME_EXTENSION, FILE_PATH_DEFAULT_EVENT_LOG, - FILE_PATH_EVENT_LOG_EXTENSION + FILE_PATH_EVENT_LOG_EXTENSION, + FILE_PATH_DISK_CONTROL_INDEX_EXTENSION }; enum application_special_type diff --git a/file_path_str.c b/file_path_str.c index 97e7e16887..dc09b6f360 100644 --- a/file_path_str.c +++ b/file_path_str.c @@ -227,6 +227,9 @@ const char *file_path_str(enum file_path_enum enum_idx) case FILE_PATH_EVENT_LOG_EXTENSION: str = ".log"; break; + case FILE_PATH_DISK_CONTROL_INDEX_EXTENSION: + str = ".ldci"; + break; case FILE_PATH_UNKNOWN: default: break; diff --git a/griffin/griffin.c b/griffin/griffin.c index 40312bb175..b4bd31a673 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -156,9 +156,10 @@ CONFIG FILE #include "../libretro-common/file/config_file_userdata.c" /*============================================================ -RUNTIME FILE +CONTENT METADATA RECORDS ============================================================ */ #include "../runtime_file.c" +#include "../disk_index_file.c" /*============================================================ ACHIEVEMENTS @@ -1641,3 +1642,8 @@ PLAYLIST NAME SANITIZATION MANUAL CONTENT SCAN ============================================================ */ #include "../manual_content_scan.c" + +/*============================================================ +DISK CONTROL INTERFACE +============================================================ */ +#include "../disk_control_interface.c" diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index fdfaafb3a8..5aaddcd2d9 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -836,6 +836,10 @@ MSG_HASH( MENU_ENUM_LABEL_VALUE_DISK_OPTIONS, "Disk Control" ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_NO_DISK, + "No disk selected" + ) MSG_HASH( MENU_ENUM_LABEL_VALUE_DONT_CARE, "Don't care" @@ -4791,6 +4795,10 @@ MSG_HASH( MSG_APPENDED_DISK, "Appended disk" ) +MSG_HASH( + MSG_FAILED_TO_APPEND_DISK, + "Failed to append disk" + ) MSG_HASH( MSG_APPLICATION_DIR, "Application Dir" @@ -5607,6 +5615,14 @@ MSG_HASH( MSG_VIRTUAL_DISK_TRAY, "virtual disk tray." ) +MSG_HASH( + MSG_VIRTUAL_DISK_TRAY_EJECT, + "eject" + ) +MSG_HASH( + MSG_VIRTUAL_DISK_TRAY_CLOSE, + "close" + ) MSG_HASH( MENU_ENUM_SUBLABEL_AUDIO_LATENCY, "Desired audio latency in milliseconds. Might not be honored if the audio driver can't provide given latency." @@ -8977,6 +8993,10 @@ MSG_HASH( MSG_FAILED_TO_SET_DISK, "Failed to set disk" ) +MSG_HASH( + MSG_FAILED_TO_SET_INITIAL_DISK, + "Failed to set last used disk..." + ) MSG_HASH( MENU_ENUM_LABEL_VALUE_QT_CORE_OPTIONS, "Core Options" diff --git a/menu/cbs/menu_cbs_get_value.c b/menu/cbs/menu_cbs_get_value.c index fea114f782..a0370c1c25 100644 --- a/menu/cbs/menu_cbs_get_value.c +++ b/menu/cbs/menu_cbs_get_value.c @@ -762,29 +762,22 @@ static void menu_action_setting_disp_set_label_menu_disk_index( const char *path, char *s2, size_t len2) { - unsigned images = 0, current = 0; - struct retro_disk_control_ext_callback *control = NULL; - rarch_system_info_t *system = runloop_get_system_info(); + unsigned images = 0; + unsigned current = 0; + rarch_system_info_t *system = runloop_get_system_info(); if (!system) return; - control = &system->disk_control_cb; - - if (!control) + if (!disk_control_enabled(&system->disk_control)) return; *w = 19; *s = '\0'; strlcpy(s2, path, len2); - if (!control->get_num_images) - return; - if (!control->get_image_index) - return; - - images = control->get_num_images(); - current = control->get_image_index(); + images = disk_control_get_num_images(&system->disk_control); + current = disk_control_get_image_index(&system->disk_control); if (current >= images) strlcpy(s, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_DISK), len); diff --git a/menu/cbs/menu_cbs_ok.c b/menu/cbs/menu_cbs_ok.c index 4dbd9cb42a..4881271f60 100644 --- a/menu/cbs/menu_cbs_ok.c +++ b/menu/cbs/menu_cbs_ok.c @@ -6116,15 +6116,7 @@ static int action_ok_disk_cycle_tray_status(const char *path, /* Get disk eject state *before* toggling drive status */ if (sys_info) - { - const struct retro_disk_control_ext_callback *control = - (const struct retro_disk_control_ext_callback*) - &sys_info->disk_control_cb; - - if (control) - if (control->get_eject_state) - disk_ejected = control->get_eject_state(); - } + disk_ejected = disk_control_get_eject_state(&sys_info->disk_control); /* Only want to display a notification if we are * going to resume content immediately after diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index fd48cb18e5..8a233614e0 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -2026,7 +2026,7 @@ static int menu_displaylist_parse_load_content_settings( } if ((!rarch_ctl(RARCH_CTL_IS_DUMMY_CORE, NULL)) - && system->disk_control_cb.get_num_images) + && disk_control_enabled(&system->disk_control)) if (menu_entries_append_enum(list, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DISK_OPTIONS), msg_hash_to_str(MENU_ENUM_LABEL_DISK_OPTIONS), @@ -3242,28 +3242,19 @@ static unsigned menu_displaylist_parse_content_information( static unsigned menu_displaylist_parse_disk_options( file_list_t *list) { - unsigned count = 0; - rarch_system_info_t *sys_info = - runloop_get_system_info(); - const struct retro_disk_control_ext_callback *control = NULL; - bool disk_ejected; + unsigned count = 0; + rarch_system_info_t *sys_info = runloop_get_system_info(); + bool disk_ejected = false; /* Sanity Check */ if (!sys_info) return count; - control = (const struct retro_disk_control_ext_callback*) - &sys_info->disk_control_cb; - - if (!control || - !control->get_num_images || - !control->get_image_index || - !control->get_eject_state || - !control->set_eject_state) + if (!disk_control_enabled(&sys_info->disk_control)) return count; /* Check whether disk is currently ejected */ - disk_ejected = control->get_eject_state(); + disk_ejected = disk_control_get_eject_state(&sys_info->disk_control); /* Always show a 'DISK_CYCLE_TRAY_STATUS' entry * > These perform the same function, but just have @@ -3294,8 +3285,9 @@ static unsigned menu_displaylist_parse_disk_options( MENU_SETTINGS_CORE_DISK_OPTIONS_DISK_INDEX, 0, 0)) count++; - /* If replace_image_index() is undefined, can stop here */ - if (!control->replace_image_index) + /* If core does not support appending images, + * can stop here */ + if (!disk_control_append_enabled(&sys_info->disk_control)) return count; /* Append image does the following: @@ -5356,25 +5348,19 @@ unsigned menu_displaylist_build_list(file_list_t *list, enum menu_displaylist_ct if (sys_info) { - const struct retro_disk_control_ext_callback *control = - (const struct retro_disk_control_ext_callback*) - &sys_info->disk_control_cb; - - /* Check that the required disk control interface - * functions are defined */ - if (control && - control->get_num_images && - control->get_image_index) + if (disk_control_enabled(&sys_info->disk_control)) { - unsigned num_images = control->get_num_images(); - unsigned current_image = control->get_image_index(); + unsigned num_images = + disk_control_get_num_images(&sys_info->disk_control); + unsigned current_image = + disk_control_get_image_index(&sys_info->disk_control); unsigned num_digits = 0; unsigned i; /* If core supports labels, index value string * should be padded to maximum width (otherwise * labels will be misaligned/ugly) */ - if (control->get_image_label) + if (disk_control_image_label_enabled(&sys_info->disk_control)) { unsigned digit_counter = num_images; do @@ -5395,9 +5381,9 @@ unsigned menu_displaylist_build_list(file_list_t *list, enum menu_displaylist_ct image_label[0] = '\0'; /* Get image label, if supported by core */ - if (control->get_image_label) - if (!control->get_image_label(i, image_label, sizeof(image_label))) - image_label[0] = '\0'; + disk_control_get_image_label( + &sys_info->disk_control, + i, image_label, sizeof(image_label)); /* Get string representation of disk index * > Note that displayed index starts at '1', diff --git a/msg_hash.h b/msg_hash.h index 31385aeeac..4f0be461a4 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -169,6 +169,7 @@ enum msg_hash_enums MSG_UNKNOWN = 0, MSG_SETTING_DISK_IN_TRAY, MSG_FAILED_TO_SET_DISK, + MSG_FAILED_TO_SET_INITIAL_DISK, MSG_NETPLAY_FAILED, MSG_UNKNOWN_NETPLAY_COMMAND_RECEIVED, MSG_CONNECTING_TO_NETPLAY_HOST, @@ -412,6 +413,7 @@ enum msg_hash_enums MSG_FAILED_TO_REMOVE_TEMPORARY_FILE, MSG_STARTING_MOVIE_PLAYBACK, MSG_APPENDED_DISK, + MSG_FAILED_TO_APPEND_DISK, MSG_SKIPPING_SRAM_LOAD, MSG_CONFIG_DIRECTORY_NOT_SET, MSG_SAVED_STATE_TO_SLOT, @@ -438,6 +440,8 @@ enum msg_hash_enums MSG_VIRTUAL_DISK_TRAY, MSG_REMOVED_DISK_FROM_TRAY, MSG_FAILED_TO_REMOVE_DISK_FROM_TRAY, + MSG_VIRTUAL_DISK_TRAY_EJECT, + MSG_VIRTUAL_DISK_TRAY_CLOSE, MSG_GOT_INVALID_DISK_INDEX, MSG_INDEX_FILE, MSG_DOWNLOADING, diff --git a/retroarch.c b/retroarch.c index 735239a4d3..426772a3d7 100644 --- a/retroarch.c +++ b/retroarch.c @@ -1601,7 +1601,7 @@ static void path_set_redirect(void) fill_pathname_dir(global->name.savefile, !string_is_empty(path_main_basename) ? path_main_basename : system && !string_is_empty(system->library_name) ? system->library_name : "", - ".srm", + file_path_str(FILE_PATH_SRM_EXTENSION), sizeof(global->name.savefile)); RARCH_LOG("%s \"%s\".\n", msg_hash_to_str(MSG_REDIRECTING_SAVEFILE_TO), @@ -1613,7 +1613,7 @@ static void path_set_redirect(void) fill_pathname_dir(global->name.savestate, !string_is_empty(path_main_basename) ? path_main_basename : system && !string_is_empty(system->library_name) ? system->library_name : "", - ".state", + file_path_str(FILE_PATH_STATE_EXTENSION), sizeof(global->name.savestate)); RARCH_LOG("%s \"%s\".\n", msg_hash_to_str(MSG_REDIRECTING_SAVESTATE_TO), @@ -1625,7 +1625,7 @@ static void path_set_redirect(void) /* FIXME: Should this optionally use system->library_name like the others? */ fill_pathname_dir(global->name.cheatfile, !string_is_empty(path_main_basename) ? path_main_basename : "", - ".state", + file_path_str(FILE_PATH_CHT_EXTENSION), sizeof(global->name.cheatfile)); RARCH_LOG("%s \"%s\".\n", msg_hash_to_str(MSG_REDIRECTING_CHEATFILE_TO), @@ -5221,126 +5221,6 @@ finish: } #endif -/** - * command_event_disk_control_set_eject: - * @new_state : Eject or close the virtual drive tray. - * false (0) : Close - * true (1) : Eject - * @print_log : Show message onscreen. - * - * Ejects/closes of the virtual drive tray. - **/ -static void command_event_disk_control_set_eject(bool new_state, bool print_log) -{ - char msg[128]; - bool error = false; - const struct retro_disk_control_ext_callback *control = NULL; - rarch_system_info_t *info = &runloop_system; - - msg[0] = '\0'; - - if (info) - control = (const struct retro_disk_control_ext_callback*)&info->disk_control_cb; - - if (!control || !control->get_num_images) - return; - - if (control->set_eject_state(new_state)) - snprintf(msg, sizeof(msg), "%s %s", - new_state ? - msg_hash_to_str(MSG_DISK_EJECTED) : - msg_hash_to_str(MSG_DISK_CLOSED), - msg_hash_to_str(MSG_VIRTUAL_DISK_TRAY)); - else - { - error = true; - snprintf(msg, sizeof(msg), "%s %s %s", - msg_hash_to_str(MSG_FAILED_TO), - new_state ? "eject" : "close", - msg_hash_to_str(MSG_VIRTUAL_DISK_TRAY)); - } - - if (!string_is_empty(msg)) - { - if (error) - RARCH_ERR("%s\n", msg); - else - RARCH_LOG("%s\n", msg); - - /* Errors should always be displayed */ - if (print_log || error) - runloop_msg_queue_push( - msg, 1, error ? 180 : 60, - true, NULL, - MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); - } -} - -/** - * command_event_disk_control_set_index: - * @idx : Index of disk to set as current. - * @print_log : Show message onscreen. - * - * Sets current disk to @index. - **/ -static void command_event_disk_control_set_index(unsigned idx, bool print_log) -{ - unsigned num_disks; - char msg[128]; - bool error = false; - const struct retro_disk_control_ext_callback *control = NULL; - rarch_system_info_t *info = &runloop_system; - - msg[0] = '\0'; - - if (info) - control = (const struct retro_disk_control_ext_callback*)&info->disk_control_cb; - - if (!control || !control->get_num_images) - return; - - num_disks = control->get_num_images(); - - if (control->set_image_index(idx)) - { - if (idx < num_disks) - snprintf(msg, sizeof(msg), "%s: %u/%u.", - msg_hash_to_str(MSG_SETTING_DISK_IN_TRAY), - idx + 1, num_disks); - else - strlcpy(msg, - msg_hash_to_str(MSG_REMOVED_DISK_FROM_TRAY), - sizeof(msg)); - } - else - { - if (idx < num_disks) - snprintf(msg, sizeof(msg), "%s %u/%u.", - msg_hash_to_str(MSG_FAILED_TO_SET_DISK), - idx + 1, num_disks); - else - strlcpy(msg, - msg_hash_to_str(MSG_FAILED_TO_REMOVE_DISK_FROM_TRAY), - sizeof(msg)); - error = true; - } - - if (!string_is_empty(msg)) - { - if (error) - RARCH_ERR("%s\n", msg); - else - RARCH_LOG("%s\n", msg); - - /* Errors should always be displayed */ - if (print_log || error) - runloop_msg_queue_push( - msg, 1, error ? 180 : 60, - true, NULL, - MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); - } -} - /** * command_event_disk_control_append_image: * @path : Path to disk image. @@ -5349,48 +5229,14 @@ static void command_event_disk_control_set_index(unsigned idx, bool print_log) **/ static bool command_event_disk_control_append_image(const char *path) { - unsigned new_idx; - char msg[128]; - struct retro_game_info info = {0}; - const struct retro_disk_control_ext_callback *control = NULL; - rarch_system_info_t *sysinfo = &runloop_system; - const char *disk_filename = NULL; + rarch_system_info_t *sys_info = &runloop_system; - msg[0] = '\0'; - - if (string_is_empty(path)) + if (!sys_info) return false; - disk_filename = path_basename(path); - - if (string_is_empty(disk_filename)) + if (!disk_control_append_image(&sys_info->disk_control, path)) return false; - if (sysinfo) - control = (const struct retro_disk_control_ext_callback*) - &sysinfo->disk_control_cb; - - if (!control) - return false; - - command_event_disk_control_set_eject(true, false); - - control->add_image_index(); - new_idx = control->get_num_images(); - if (!new_idx) - return false; - new_idx--; - - info.path = path; - control->replace_image_index(new_idx, &info); - - snprintf(msg, sizeof(msg), "%s: %s", msg_hash_to_str(MSG_APPENDED_DISK), disk_filename); - RARCH_LOG("%s\n", msg); - /* This message should always be displayed, since - * the menu itself does not provide sufficient - * visual feedback */ - runloop_msg_queue_push(msg, 0, 120, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); - #ifdef HAVE_THREADS retroarch_autosave_deinit(); #endif @@ -5407,80 +5253,10 @@ static bool command_event_disk_control_append_image(const char *path) } command_event(CMD_EVENT_AUTOSAVE_INIT, NULL); - command_event_disk_control_set_index(new_idx, false); - command_event_disk_control_set_eject(false, false); return true; } -/** - * command_event_check_disk_prev: - * @control : Handle to disk control handle. - * @print_log : Show message onscreen. - * - * Perform disk cycle to previous index action (Core Disk Options). - **/ -static void command_event_check_disk_prev( - const struct retro_disk_control_ext_callback *control, bool print_log) -{ - unsigned num_disks = 0; - unsigned current = 0; - bool disk_prev_enable = false; - - if (!control || !control->get_num_images) - return; - if (!control->get_image_index) - return; - - num_disks = control->get_num_images(); - current = control->get_image_index(); - disk_prev_enable = num_disks && num_disks != UINT_MAX; - - if (!disk_prev_enable) - { - RARCH_ERR("%s.\n", msg_hash_to_str(MSG_GOT_INVALID_DISK_INDEX)); - return; - } - - if (current > 0) - current--; - command_event_disk_control_set_index(current, print_log); -} - -/** - * command_event_check_disk_next: - * @control : Handle to disk control handle. - * @print_log : Show message onscreen. - * - * Perform disk cycle to next index action (Core Disk Options). - **/ -static void command_event_check_disk_next( - const struct retro_disk_control_ext_callback *control, bool print_log) -{ - unsigned num_disks = 0; - unsigned current = 0; - bool disk_next_enable = false; - - if (!control || !control->get_num_images) - return; - if (!control->get_image_index) - return; - - num_disks = control->get_num_images(); - current = control->get_image_index(); - disk_next_enable = num_disks && num_disks != UINT_MAX; - - if (!disk_next_enable) - { - RARCH_ERR("%s.\n", msg_hash_to_str(MSG_GOT_INVALID_DISK_INDEX)); - return; - } - - if (current < num_disks - 1) - current++; - command_event_disk_control_set_index(current, print_log); -} - /** * event_set_volume: * @gain : amount of gain to be applied to current volume level. @@ -6033,9 +5809,18 @@ static bool command_event_init_core(enum rarch_core_type type) current_core.retro_init(); current_core.inited = true; + /* Attempt to set initial disk index */ + disk_control_set_initial_index( + &runloop_system.disk_control, + path_get(RARCH_PATH_CONTENT), + dir_get(RARCH_DIR_CURRENT_SAVEFILE)); + if (!event_init_content()) return false; + /* Verify that initial disk index was set correctly */ + disk_control_verify_initial_index(&runloop_system.disk_control); + if (!core_load(settings->uints.input_poll_type_behavior)) return false; @@ -6816,11 +6601,16 @@ bool command_event(enum event_command cmd, void *data) bool contentless = false; bool is_inited = false; content_ctx_info_t content_info = {0}; + rarch_system_info_t *sys_info = &runloop_system; content_get_status(&contentless, &is_inited); runloop_core_running = false; + /* Save last selected disk index, if required */ + if (sys_info) + disk_control_save_image_index(&sys_info->disk_control); + command_event_runtime_log_deinit(); command_event_save_auto_state(); command_event_disable_overrides(); @@ -7136,6 +6926,12 @@ TODO: Add a setting for these tweaks */ case CMD_EVENT_CORE_DEINIT: { struct retro_hw_render_callback *hwr = NULL; + rarch_system_info_t *sys_info = &runloop_system; + + /* Save last selected disk index, if required */ + if (sys_info) + disk_control_save_image_index(&sys_info->disk_control); + command_event_runtime_log_deinit(); content_reset_savestate_backups(); hwr = video_driver_get_hw_context_internal(); @@ -7584,33 +7380,29 @@ TODO: Add a setting for these tweaks */ break; case CMD_EVENT_DISK_EJECT_TOGGLE: { - rarch_system_info_t *info = &runloop_system; - bool *show_msg = (bool*)data; + rarch_system_info_t *sys_info = &runloop_system; + bool *show_msg = (bool*)data; - if (info && info->disk_control_cb.get_num_images) + if (!sys_info) + return false; + + if (disk_control_enabled(&sys_info->disk_control)) { - const struct retro_disk_control_ext_callback *control = - (const struct retro_disk_control_ext_callback*) - &info->disk_control_cb; + bool eject = !disk_control_get_eject_state(&sys_info->disk_control); + bool verbose = true; + bool refresh = false; - if (control) - { - bool new_state = !control->get_eject_state(); - bool print_log = true; - bool refresh = false; + if (show_msg) + verbose = *show_msg; - if (show_msg) - print_log = *show_msg; - - command_event_disk_control_set_eject(new_state, print_log); + disk_control_set_eject_state(&sys_info->disk_control, eject, verbose); #if defined(HAVE_MENU) - /* It is necessary to refresh the disk options - * menu when toggling the tray state */ - menu_entries_ctl(MENU_ENTRIES_CTL_SET_REFRESH, &refresh); - menu_driver_ctl(RARCH_MENU_CTL_SET_PREVENT_POPULATE, NULL); + /* It is necessary to refresh the disk options + * menu when toggling the tray state */ + menu_entries_ctl(MENU_ENTRIES_CTL_SET_REFRESH, &refresh); + menu_driver_ctl(RARCH_MENU_CTL_SET_PREVENT_POPULATE, NULL); #endif - } } else runloop_msg_queue_push( @@ -7621,26 +7413,20 @@ TODO: Add a setting for these tweaks */ break; case CMD_EVENT_DISK_NEXT: { - rarch_system_info_t *info = &runloop_system; - bool *show_msg = (bool*)data; + rarch_system_info_t *sys_info = &runloop_system; + bool *show_msg = (bool*)data; - if (info && info->disk_control_cb.get_num_images) + if (!sys_info) + return false; + + if (disk_control_enabled(&sys_info->disk_control)) { - const struct retro_disk_control_ext_callback *control = - (const struct retro_disk_control_ext_callback*) - &info->disk_control_cb; - bool print_log = true; - - if (!control) - return false; - - if (!control->get_eject_state()) - return false; + bool verbose = true; if (show_msg) - print_log = *show_msg; + verbose = *show_msg; - command_event_check_disk_next(control, print_log); + disk_control_set_index_next(&sys_info->disk_control, verbose); } else runloop_msg_queue_push( @@ -7651,26 +7437,20 @@ TODO: Add a setting for these tweaks */ break; case CMD_EVENT_DISK_PREV: { - rarch_system_info_t *info = &runloop_system; - bool *show_msg = (bool*)data; + rarch_system_info_t *sys_info = &runloop_system; + bool *show_msg = (bool*)data; - if (info && info->disk_control_cb.get_num_images) + if (!sys_info) + return false; + + if (disk_control_enabled(&sys_info->disk_control)) { - const struct retro_disk_control_ext_callback *control = - (const struct retro_disk_control_ext_callback*) - &info->disk_control_cb; - bool print_log = true; - - if (!control) - return false; - - if (!control->get_eject_state()) - return false; + bool verbose = true; if (show_msg) - print_log = *show_msg; + verbose = *show_msg; - command_event_check_disk_prev(control, print_log); + disk_control_set_index_prev(&sys_info->disk_control, verbose); } else runloop_msg_queue_push( @@ -7681,25 +7461,16 @@ TODO: Add a setting for these tweaks */ break; case CMD_EVENT_DISK_INDEX: { - rarch_system_info_t *info = &runloop_system; - unsigned *index = (unsigned*)data; + rarch_system_info_t *sys_info = &runloop_system; + unsigned *index = (unsigned*)data; - if (!index) + if (!sys_info || !index) return false; - if (info && info->disk_control_cb.get_num_images) - { - const struct retro_disk_control_ext_callback *control = - (const struct retro_disk_control_ext_callback*) - &info->disk_control_cb; - - if (!control) - return false; - - /* Note: Menu itself provides visual feedback - no - * need to print info message to screen */ - command_event_disk_control_set_index(*index, false); - } + /* Note: Menu itself provides visual feedback - no + * need to print info message to screen */ + if (disk_control_enabled(&sys_info->disk_control)) + disk_control_set_index(&sys_info->disk_control, *index, false); else runloop_msg_queue_push( msg_hash_to_str(MSG_CORE_DOES_NOT_SUPPORT_DISK_OPTIONS), @@ -9815,30 +9586,25 @@ static bool rarch_environment_cb(unsigned cmd, void *data) const struct retro_disk_control_callback *control_cb = (const struct retro_disk_control_callback*)data; - if (control_cb && system) + if (system) { RARCH_LOG("[Environ]: SET_DISK_CONTROL_INTERFACE.\n"); - - system->disk_control_cb.set_eject_state = control_cb->set_eject_state; - system->disk_control_cb.get_eject_state = control_cb->get_eject_state; - system->disk_control_cb.get_image_index = control_cb->get_image_index; - system->disk_control_cb.set_image_index = control_cb->set_image_index; - system->disk_control_cb.get_num_images = control_cb->get_num_images; - system->disk_control_cb.replace_image_index = control_cb->replace_image_index; - system->disk_control_cb.add_image_index = control_cb->add_image_index; - - system->disk_control_cb.set_initial_image = NULL; - system->disk_control_cb.get_image_path = NULL; - system->disk_control_cb.get_image_label = NULL; + disk_control_set_callback(&system->disk_control, control_cb); } } break; case RETRO_ENVIRONMENT_SET_DISK_CONTROL_EXT_INTERFACE: - RARCH_LOG("[Environ]: SET_DISK_CONTROL_EXT_INTERFACE.\n"); - if (system) - system->disk_control_cb = - *(const struct retro_disk_control_ext_callback*)data; + { + const struct retro_disk_control_ext_callback *control_cb = + (const struct retro_disk_control_ext_callback*)data; + + if (system) + { + RARCH_LOG("[Environ]: SET_DISK_CONTROL_EXT_INTERFACE.\n"); + disk_control_set_ext_callback(&system->disk_control, control_cb); + } + } break; case RETRO_ENVIRONMENT_GET_PREFERRED_HW_RENDER: diff --git a/runtime_file.c b/runtime_file.c index e9018f7faf..d6af1644f5 100644 --- a/runtime_file.c +++ b/runtime_file.c @@ -409,7 +409,7 @@ runtime_log_t *runtime_log_init( /* Build final log file path */ fill_pathname_join(log_file_path, log_file_dir, content_name, sizeof(log_file_path)); - strlcat(log_file_path, ".lrtl", sizeof(log_file_path)); + strlcat(log_file_path, file_path_str(FILE_PATH_RUNTIME_EXTENSION), sizeof(log_file_path)); if (string_is_empty(log_file_path)) return NULL;