mirror of
https://github.com/libretro/RetroArch
synced 2025-01-31 15:32:59 +00:00
Merge branch 'master' of https://github.com/libretro/RetroArch
This commit is contained in:
commit
bf5d3437f0
@ -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
5
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
|
||||
{
|
||||
|
774
disk_control_interface.c
Normal file
774
disk_control_interface.c
Normal 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
178
disk_control_interface.h
Normal 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
523
disk_index_file.c
Normal 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
76
disk_index_file.h
Normal 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
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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',
|
||||
|
@ -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,
|
||||
|
392
retroarch.c
392
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:
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user