aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--frontend/libretro.c285
-rw-r--r--libretro-common/include/libretro.h120
2 files changed, 370 insertions, 35 deletions
diff --git a/frontend/libretro.c b/frontend/libretro.c
index ab85421..64e4145 100644
--- a/frontend/libretro.c
+++ b/frontend/libretro.c
@@ -848,15 +848,72 @@ void retro_cheat_set(unsigned index, bool enabled, const char *code)
Cheats[index].Enabled = enabled;
}
+// just in case, maybe a win-rt port in the future?
+#ifdef _WIN32
+#define SLASH '\\'
+#else
+#define SLASH '/'
+#endif
+
+#ifndef PATH_MAX
+#define PATH_MAX 4096
+#endif
+
/* multidisk support */
+static unsigned int disk_initial_index;
+static char disk_initial_path[PATH_MAX];
static bool disk_ejected;
static unsigned int disk_current_index;
static unsigned int disk_count;
static struct disks_state {
char *fname;
+ char *flabel;
int internal_index; // for multidisk eboots
} disks[8];
+static void get_disk_label(char *disk_label, const char *disk_path, size_t len)
+{
+ const char *base = NULL;
+
+ if (!disk_path || (*disk_path == '\0'))
+ return;
+
+ base = strrchr(disk_path, SLASH);
+ if (!base)
+ base = disk_path;
+
+ if (*base == SLASH)
+ base++;
+
+ strncpy(disk_label, base, len - 1);
+ disk_label[len - 1] = '\0';
+
+ char *ext = strrchr(disk_label, '.');
+ if (ext)
+ *ext = '\0';
+}
+
+static void disk_init(void)
+{
+ size_t i;
+
+ disk_ejected = false;
+ disk_current_index = 0;
+ disk_count = 0;
+
+ for (i = 0; i < sizeof(disks) / sizeof(disks[0]); i++) {
+ if (disks[i].fname != NULL) {
+ free(disks[i].fname);
+ disks[i].fname = NULL;
+ }
+ if (disks[i].flabel != NULL) {
+ free(disks[i].flabel);
+ disks[i].flabel = NULL;
+ }
+ disks[i].internal_index = 0;
+ }
+}
+
static bool disk_set_eject_state(bool ejected)
{
// weird PCSX API..
@@ -927,18 +984,29 @@ static unsigned int disk_get_num_images(void)
static bool disk_replace_image_index(unsigned index,
const struct retro_game_info *info)
{
- char *old_fname;
- bool ret = true;
+ char *old_fname = NULL;
+ char *old_flabel = NULL;
+ bool ret = true;
if (index >= sizeof(disks) / sizeof(disks[0]))
return false;
- old_fname = disks[index].fname;
- disks[index].fname = NULL;
+ old_fname = disks[index].fname;
+ old_flabel = disks[index].flabel;
+
+ disks[index].fname = NULL;
+ disks[index].flabel = NULL;
disks[index].internal_index = 0;
if (info != NULL) {
+ char disk_label[PATH_MAX];
+ disk_label[0] = '\0';
+
disks[index].fname = strdup(info->path);
+
+ get_disk_label(disk_label, info->path, PATH_MAX);
+ disks[index].flabel = strdup(disk_label);
+
if (index == disk_current_index)
ret = disk_set_image_index(index);
}
@@ -946,6 +1014,9 @@ static bool disk_replace_image_index(unsigned index,
if (old_fname != NULL)
free(old_fname);
+ if (old_flabel != NULL)
+ free(old_flabel);
+
return ret;
}
@@ -958,6 +1029,64 @@ static bool disk_add_image_index(void)
return true;
}
+static bool disk_set_initial_image(unsigned index, const char *path)
+{
+ if (index >= sizeof(disks) / sizeof(disks[0]))
+ return false;
+
+ if (!path || (*path == '\0'))
+ return false;
+
+ disk_initial_index = index;
+
+ strncpy(disk_initial_path, path, sizeof(disk_initial_path) - 1);
+ disk_initial_path[sizeof(disk_initial_path) - 1] = '\0';
+
+ return true;
+}
+
+static bool disk_get_image_path(unsigned index, char *path, size_t len)
+{
+ const char *fname = NULL;
+
+ if (len < 1)
+ return false;
+
+ if (index >= sizeof(disks) / sizeof(disks[0]))
+ return false;
+
+ fname = disks[index].fname;
+
+ if (!fname || (*fname == '\0'))
+ return false;
+
+ strncpy(path, fname, len - 1);
+ path[len - 1] = '\0';
+
+ return true;
+}
+
+static bool disk_get_image_label(unsigned index, char *label, size_t len)
+{
+ const char *flabel = NULL;
+
+ if (len < 1)
+ return false;
+
+ if (index >= sizeof(disks) / sizeof(disks[0]))
+ return false;
+
+ flabel = disks[index].flabel;
+
+ if (!flabel || (*flabel == '\0'))
+ return false;
+
+ strncpy(label, flabel, len - 1);
+ label[len - 1] = '\0';
+
+ return true;
+}
+
static struct retro_disk_control_callback disk_control = {
.set_eject_state = disk_set_eject_state,
.get_eject_state = disk_get_eject_state,
@@ -968,16 +1097,18 @@ static struct retro_disk_control_callback disk_control = {
.add_image_index = disk_add_image_index,
};
-// just in case, maybe a win-rt port in the future?
-#ifdef _WIN32
-#define SLASH '\\'
-#else
-#define SLASH '/'
-#endif
-
-#ifndef PATH_MAX
-#define PATH_MAX 4096
-#endif
+static struct retro_disk_control_ext_callback disk_control_ext = {
+ .set_eject_state = disk_set_eject_state,
+ .get_eject_state = disk_get_eject_state,
+ .get_image_index = disk_get_image_index,
+ .set_image_index = disk_set_image_index,
+ .get_num_images = disk_get_num_images,
+ .replace_image_index = disk_replace_image_index,
+ .add_image_index = disk_add_image_index,
+ .set_initial_image = disk_set_initial_image,
+ .get_image_path = disk_get_image_path,
+ .get_image_label = disk_get_image_label,
+};
static char base_dir[1024];
@@ -1001,8 +1132,16 @@ static bool read_m3u(const char *file)
if (line[0] != '\0')
{
+ char disk_label[PATH_MAX];
+ disk_label[0] = '\0';
+
snprintf(name, sizeof(name), "%s%c%s", base_dir, SLASH, line);
- disks[disk_count++].fname = strdup(name);
+ disks[disk_count].fname = strdup(name);
+
+ get_disk_label(disk_label, name, PATH_MAX);
+ disks[disk_count].flabel = strdup(disk_label);
+
+ disk_count++;
}
}
@@ -1073,6 +1212,7 @@ static void set_retro_memmap(void)
bool retro_load_game(const struct retro_game_info *info)
{
size_t i;
+ unsigned int cd_index = 0;
bool is_m3u = (strcasestr(info->path, ".m3u") != NULL);
struct retro_input_descriptor desc[] = {
@@ -1269,15 +1409,8 @@ bool retro_load_game(const struct retro_game_info *info)
plugins_opened = 0;
}
- for (i = 0; i < sizeof(disks) / sizeof(disks[0]); i++) {
- if (disks[i].fname != NULL) {
- free(disks[i].fname);
- disks[i].fname = NULL;
- }
- disks[i].internal_index = 0;
- }
+ disk_init();
- disk_current_index = 0;
extract_directory(base_dir, info->path, sizeof(base_dir));
if (is_m3u) {
@@ -1286,11 +1419,30 @@ bool retro_load_game(const struct retro_game_info *info)
return false;
}
} else {
+ char disk_label[PATH_MAX];
+ disk_label[0] = '\0';
+
disk_count = 1;
disks[0].fname = strdup(info->path);
+
+ get_disk_label(disk_label, info->path, PATH_MAX);
+ disks[0].flabel = strdup(disk_label);
+ }
+
+ /* If this is an M3U file, attempt to set the
+ * initial disk image */
+ if (is_m3u &&
+ (disk_initial_index > 0) &&
+ (disk_initial_index < disk_count)) {
+ const char *fname = disks[disk_initial_index].fname;
+
+ if (fname && (*fname != '\0'))
+ if (strcmp(disk_initial_path, fname) == 0)
+ cd_index = disk_initial_index;
}
- set_cd_image(disks[0].fname);
+ set_cd_image(disks[cd_index].fname);
+ disk_current_index = cd_index;
/* have to reload after set_cd_image for correct cdr plugin */
if (LoadPlugins() == -1) {
@@ -1306,6 +1458,69 @@ bool retro_load_game(const struct retro_game_info *info)
return false;
}
+ /* Handle multi-disk images (i.e. PBP)
+ * > Cannot do this until after OpenPlugins() is
+ * called (since this sets the value of
+ * cdrIsoMultidiskCount) */
+ if (!is_m3u && (cdrIsoMultidiskCount > 1)) {
+ disk_count = cdrIsoMultidiskCount < 8 ? cdrIsoMultidiskCount : 8;
+
+ /* Small annoyance: We need to change the label
+ * of disk 0, so have to clear existing entries */
+ if (disks[0].fname != NULL)
+ free(disks[0].fname);
+ disks[0].fname = NULL;
+
+ if (disks[0].flabel != NULL)
+ free(disks[0].flabel);
+ disks[0].flabel = NULL;
+
+ for (i = 0; i < sizeof(disks) / sizeof(disks[0]) && i < cdrIsoMultidiskCount; i++) {
+ char disk_name[PATH_MAX];
+ char disk_label[PATH_MAX];
+ disk_name[0] = '\0';
+ disk_label[0] = '\0';
+
+ disks[i].fname = strdup(info->path);
+
+ get_disk_label(disk_name, info->path, PATH_MAX);
+ snprintf(disk_label, sizeof(disk_label), "%s #%u", disk_name, (unsigned)i + 1);
+ disks[i].flabel = strdup(disk_label);
+
+ disks[i].internal_index = i;
+ }
+
+ /* This is not an M3U file, so initial disk
+ * image has not yet been set - attempt to
+ * do so now */
+ if ((disk_initial_index > 0) &&
+ (disk_initial_index < disk_count)) {
+ const char *fname = disks[disk_initial_index].fname;
+
+ if (fname && (*fname != '\0'))
+ if (strcmp(disk_initial_path, fname) == 0)
+ cd_index = disk_initial_index;
+ }
+
+ if (cd_index > 0) {
+ CdromId[0] = '\0';
+ CdromLabel[0] = '\0';
+
+ cdrIsoMultidiskSelect = disks[cd_index].internal_index;
+ disk_current_index = cd_index;
+ set_cd_image(disks[cd_index].fname);
+
+ if (ReloadCdromPlugin() < 0) {
+ log_cb(RETRO_LOG_INFO, "failed to reload cdr plugins\n");
+ return false;
+ }
+ if (CDR_open() < 0) {
+ log_cb(RETRO_LOG_INFO, "failed to open cdr plugin\n");
+ return false;
+ }
+ }
+ }
+
plugin_call_rearmed_cbs();
dfinput_activate();
@@ -1322,15 +1537,6 @@ bool retro_load_game(const struct retro_game_info *info)
}
emu_on_new_cd(0);
- // multidisk images
- if (!is_m3u) {
- disk_count = cdrIsoMultidiskCount < 8 ? cdrIsoMultidiskCount : 8;
- for (i = 1; i < sizeof(disks) / sizeof(disks[0]) && i < cdrIsoMultidiskCount; i++) {
- disks[i].fname = strdup(info->path);
- disks[i].internal_index = i;
- }
- }
-
set_retro_memmap();
return true;
@@ -2411,6 +2617,7 @@ static void loadPSXBios(void)
void retro_init(void)
{
+ unsigned dci_version = 0;
struct retro_rumble_interface rumble;
int ret;
@@ -2456,7 +2663,13 @@ void retro_init(void)
loadPSXBios();
environ_cb(RETRO_ENVIRONMENT_GET_CAN_DUPE, &vout_can_dupe);
- environ_cb(RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE, &disk_control);
+
+ disk_initial_index = 0;
+ disk_initial_path[0] = '\0';
+ if (environ_cb(RETRO_ENVIRONMENT_GET_DISK_CONTROL_INTERFACE_VERSION, &dci_version) && (dci_version >= 1))
+ environ_cb(RETRO_ENVIRONMENT_SET_DISK_CONTROL_EXT_INTERFACE, &disk_control_ext);
+ else
+ environ_cb(RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE, &disk_control);
rumble_cb = NULL;
if (environ_cb(RETRO_ENVIRONMENT_GET_RUMBLE_INTERFACE, &rumble))
@@ -2501,6 +2714,10 @@ void retro_deinit(void)
deinit_vita_mmap();
#endif
libretro_supports_bitmasks = false;
+
+ /* Have to reset disks struct, otherwise
+ * fnames/flabels will leak memory */
+ disk_init();
}
#ifdef VITA
diff --git a/libretro-common/include/libretro.h b/libretro-common/include/libretro.h
index d0f1042..4ddb6e8 100644
--- a/libretro-common/include/libretro.h
+++ b/libretro-common/include/libretro.h
@@ -1246,6 +1246,47 @@ enum retro_mod
* default when calling SET_VARIABLES/SET_CORE_OPTIONS.
*/
+#define RETRO_ENVIRONMENT_GET_PREFERRED_HW_RENDER 56
+ /* unsigned * --
+ *
+ * Allows an implementation to ask frontend preferred hardware
+ * context to use. Core should use this information to deal
+ * with what specific context to request with SET_HW_RENDER.
+ *
+ * 'data' points to an unsigned variable
+ */
+
+#define RETRO_ENVIRONMENT_GET_DISK_CONTROL_INTERFACE_VERSION 57
+ /* unsigned * --
+ * Unsigned value is the API version number of the disk control
+ * interface supported by the frontend. If callback return false,
+ * API version is assumed to be 0.
+ *
+ * In legacy code, the disk control interface is defined by passing
+ * a struct of type retro_disk_control_callback to
+ * RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE.
+ * This may be still be done regardless of the disk control
+ * interface version.
+ *
+ * If version is >= 1 however, the disk control interface may
+ * instead be defined by passing a struct of type
+ * retro_disk_control_ext_callback to
+ * RETRO_ENVIRONMENT_SET_DISK_CONTROL_EXT_INTERFACE.
+ * This allows the core to provide additional information about
+ * disk images to the frontend and/or enables extra
+ * disk control functionality by the frontend.
+ */
+
+#define RETRO_ENVIRONMENT_SET_DISK_CONTROL_EXT_INTERFACE 58
+ /* const struct retro_disk_control_ext_callback * --
+ * Sets an interface which frontend can use to eject and insert
+ * disk images, and also obtain information about individual
+ * disk image files registered by the core.
+ * This is used for games which consist of multiple images and
+ * must be manually swapped out by the user (e.g. PSX, floppy disk
+ * based systems).
+ */
+
/* VFS functionality */
/* File paths:
@@ -1922,6 +1963,10 @@ enum retro_sensor_action
{
RETRO_SENSOR_ACCELEROMETER_ENABLE = 0,
RETRO_SENSOR_ACCELEROMETER_DISABLE,
+ RETRO_SENSOR_GYROSCOPE_ENABLE,
+ RETRO_SENSOR_GYROSCOPE_DISABLE,
+ RETRO_SENSOR_ILLUMINANCE_ENABLE,
+ RETRO_SENSOR_ILLUMINANCE_DISABLE,
RETRO_SENSOR_DUMMY = INT_MAX
};
@@ -1930,6 +1975,10 @@ enum retro_sensor_action
#define RETRO_SENSOR_ACCELEROMETER_X 0
#define RETRO_SENSOR_ACCELEROMETER_Y 1
#define RETRO_SENSOR_ACCELEROMETER_Z 2
+#define RETRO_SENSOR_GYROSCOPE_X 3
+#define RETRO_SENSOR_GYROSCOPE_Y 4
+#define RETRO_SENSOR_GYROSCOPE_Z 5
+#define RETRO_SENSOR_ILLUMINANCE 6
typedef bool (RETRO_CALLCONV *retro_set_sensor_state_t)(unsigned port,
enum retro_sensor_action action, unsigned rate);
@@ -2287,7 +2336,8 @@ struct retro_keyboard_callback
retro_keyboard_event_t callback;
};
-/* Callbacks for RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE.
+/* Callbacks for RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE &
+ * RETRO_ENVIRONMENT_SET_DISK_CONTROL_EXT_INTERFACE.
* Should be set for implementations which can swap out multiple disk
* images in runtime.
*
@@ -2345,6 +2395,53 @@ typedef bool (RETRO_CALLCONV *retro_replace_image_index_t)(unsigned index,
* with replace_image_index. */
typedef bool (RETRO_CALLCONV *retro_add_image_index_t)(void);
+/* Sets initial image to insert in drive when calling
+ * core_load_game().
+ * Since we cannot pass the initial index when loading
+ * content (this would require a major API change), this
+ * is set by the frontend *before* calling the core's
+ * retro_load_game()/retro_load_game_special() implementation.
+ * A core should therefore cache the index/path values and handle
+ * them inside retro_load_game()/retro_load_game_special().
+ * - If 'index' is invalid (index >= get_num_images()), the
+ * core should ignore the set value and instead use 0
+ * - 'path' is used purely for error checking - i.e. when
+ * content is loaded, the core should verify that the
+ * disk specified by 'index' has the specified file path.
+ * This is to guard against auto selecting the wrong image
+ * if (for example) the user should modify an existing M3U
+ * playlist. We have to let the core handle this because
+ * set_initial_image() must be called before loading content,
+ * i.e. the frontend cannot access image paths in advance
+ * and thus cannot perform the error check itself.
+ * If set path and content path do not match, the core should
+ * ignore the set 'index' value and instead use 0
+ * Returns 'false' if index or 'path' are invalid, or core
+ * does not support this functionality
+ */
+typedef bool (RETRO_CALLCONV *retro_set_initial_image_t)(unsigned index, const char *path);
+
+/* Fetches the path of the specified disk image file.
+ * Returns 'false' if index is invalid (index >= get_num_images())
+ * or path is otherwise unavailable.
+ */
+typedef bool (RETRO_CALLCONV *retro_get_image_path_t)(unsigned index, char *path, size_t len);
+
+/* Fetches a core-provided 'label' for the specified disk
+ * image file. In the simplest case this may be a file name
+ * (without extension), but for cores with more complex
+ * content requirements information may be provided to
+ * facilitate user disk swapping - for example, a core
+ * running floppy-disk-based content may uniquely label
+ * save disks, data disks, level disks, etc. with names
+ * corresponding to in-game disk change prompts (so the
+ * frontend can provide better user guidance than a 'dumb'
+ * disk index value).
+ * Returns 'false' if index is invalid (index >= get_num_images())
+ * or label is otherwise unavailable.
+ */
+typedef bool (RETRO_CALLCONV *retro_get_image_label_t)(unsigned index, char *label, size_t len);
+
struct retro_disk_control_callback
{
retro_set_eject_state_t set_eject_state;
@@ -2358,6 +2455,27 @@ struct retro_disk_control_callback
retro_add_image_index_t add_image_index;
};
+struct retro_disk_control_ext_callback
+{
+ retro_set_eject_state_t set_eject_state;
+ retro_get_eject_state_t get_eject_state;
+
+ retro_get_image_index_t get_image_index;
+ retro_set_image_index_t set_image_index;
+ retro_get_num_images_t get_num_images;
+
+ retro_replace_image_index_t replace_image_index;
+ retro_add_image_index_t add_image_index;
+
+ /* NOTE: Frontend will only attempt to record/restore
+ * last used disk index if both set_initial_image()
+ * and get_image_path() are implemented */
+ retro_set_initial_image_t set_initial_image; /* Optional - may be NULL */
+
+ retro_get_image_path_t get_image_path; /* Optional - may be NULL */
+ retro_get_image_label_t get_image_label; /* Optional - may be NULL */
+};
+
enum retro_pixel_format
{
/* 0RGB1555, native endian.