This commit is contained in:
twinaphex 2020-01-22 20:09:33 +01:00
commit bf5d3437f0
16 changed files with 1698 additions and 373 deletions

View File

@ -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

5
core.h
View File

@ -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
{

774
disk_control_interface.c Normal file
View File

@ -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 <string/stdstring.h>
#include <file/file_path.h>
#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);
}

178
disk_control_interface.h Normal file
View File

@ -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 <retro_common_api.h>
#include <libretro.h>
#include <boolean.h>
#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

523
disk_index_file.c Normal file
View File

@ -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 <file/file_path.h>
#include <string/stdstring.h>
#include <streams/file_stream.h>
#include <formats/jsonsax_full.h>
#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;
}

76
disk_index_file.h Normal file
View File

@ -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 <retro_common_api.h>
#include <libretro.h>
#include <retro_miscellaneous.h>
#include <boolean.h>
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

View File

@ -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

View File

@ -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;

View File

@ -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"

View File

@ -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"

View File

@ -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);

View File

@ -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

View File

@ -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',

View File

@ -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,

View File

@ -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:

View File

@ -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;