libretro / RetroArch

Cross-platform, sophisticated frontend for the libretro API. Licensed GPLv3.
http://www.libretro.com
GNU General Public License v3.0
10.31k stars 1.84k forks source link

RFC: Runtime Media Control #7876

Open andres-asm opened 5 years ago

andres-asm commented 5 years ago

Now that subsystem works, and the media control interface works I've been thinking that cores could benefit from better ways to control it's media input and output.

So this is an implementation idea for an interface that would support this use case (also paving the way for selectable BIOS files)

The existing disk control interface sorta works for this already, but fact is, it comes from a time were the API was... unidirectional. The idea here is that the core can tell the frontend it has a certain number of media slots that can be populated with media, and the API also provides a function the frontend can use to set media on a particular slot.

Let me know what you think

--- a/include/libretro.h
+++ b/include/libretro.h
@@ -1077,6 +1077,11 @@ enum retro_mod
                                             * fastforwarding mode.
                                             */

+#define RETRO_ENVIRONMENT_SET_MEDIA_SLOT_INFO (50 | RETRO_ENVIRONMENT_EXPERIMENTAL)
+                                           /* const struct retro_media_slot_info * --
+                                            * This environment call introduces the concept of media slots.
+                                            * The purpose of this is to support emulators that have one or more */
+
 /* VFS functionality */

 /* File paths:
@@ -1474,6 +1479,27 @@ struct retro_controller_info
    unsigned num_types;
 };

+struct retro_media_slot_info
+{
+   /* Describes what the media slot type is (cdrom, floppy, memory card slot, etc) */
+   const char *desc;
+
+      /* A computer friendly short string identifier for the slot type.*/
+   const char *ident;
+
+   /* Same definition as retro_get_system_info(). */
+   const char *valid_extensions;
+
+   /* Same definition as retro_get_system_info(). */
+   bool need_fullpath;
+
+   /* Same definition as retro_get_system_info(). */
+   bool block_extract;
+
+    /* Number of media slots that this particular media slot type supports. */
+   unsigned slots;
+};
+
 struct retro_subsystem_memory_info
 {
    /* The extension associated with a memory type, e.g. "psram". */
@@ -2464,6 +2490,9 @@ RETRO_API unsigned retro_get_region(void);
 RETRO_API void *retro_get_memory_data(unsigned id);
 RETRO_API size_t retro_get_memory_size(unsigned id);

+/* Set media used for a particular slot of a media slot type */
+RETRO_API void retro_set_media_slot_device(const char* ident, const struct retro_game_info *info, unsigned slot);
+
 #ifdef __cplusplus
 }

Example for mednafen psx memory cards:

static cons struct retro retro_media_control_info[] = {
    {"Memory Card", "memcard", "srm|mcr", true, true, 2},
    { NULL },
}

So the frontend would be a menu, say media slots or whatever better naming we figure out In that menu you'd have

Media Slots:

Memory Card #1: gamename.mcr.1
Memory Card #2: gamename.mcr.2

Ideally this particular case would be populated at startup, so the memcards are set with the default memcards for each slot (this may not be the case in all applications, but for memcards it's feasible) Then the user would be able to change the memcard for another from another game.

A practical application of this would be loading a GT1 save into GT2.

Say you start GT2, and then you want to import your GT1 save, then you would go to the menu and Insert the memory card in one of the slots.

That would result on the frontend calling this on the core:

retro_game_info *info;

info->path = "/path/to/gt1.mcr"
/* because it needs_fullpath the rest are irrelevant */
info->data = NULL
info->size = 0
info->meta = NULL

retro_set_media_slot_device("memcard", info, 2);

This would set the gt1.mcr "memcard" into memcard slot # 2

Example for dosbox:

static cons struct retro retro_media_control_info[] = {
    {"Floppy Disk Drive", "floppy", "img",     true, true, 2},
    {"CD-ROM Drive",      "cdrom",  "cue|iso", true, true, 1},
    { NULL },
}

Frontend menu:

Media Slots:

Floppy Disk Drive #1: empty
Floppy Disk Drive #2: empty
CD-ROM Drive: empty       ----> if it's only one of a type don't show #s

In this case you could also populate the slot at load by actually loading the img at startup

Example for gba e-reader cards, and gba other gba input/output peripheals

static cons struct retro retro_media_control_info[] = {
    {"E-Reader",    "ereader", "img",     true, true, 1},
    {"GBA Camera",  "camera",  "png",     true, true, 1},
    {"GBA Printer", "printer", "png",     true, true, 1},
    { NULL },
}

This case is particular and needs further thought, because one of the devicees is output, so it wouldn't be a valid pre-existing file but a file that would be created as output, still the API would support that as long as there is a way to "Create new file", maybe I can add a field to the api to indicate if it's an output file

Media Slots:

E-Reader: empty
GBA Camera: empty
GBA Printer: empty
andres-asm commented 5 years ago

A particular thing that I want to address and I don't know how to is HDD mounting in DOSBox. DOSBox needs to know the disk geometry before mounting an image so maybe we need a field were a user can enter arbitrary formatted data

DOSBox in particular needs:

-size [sectorsbytesize, sectorsperhead, heads, cylinders -u DRIVE]

Or maybe we can have a struct for the parameters that may be required by a particular media type

struct retro_media_info
{
    const char* key
    const char* value
}

These fields would always be text entry, and would be a pretty niche/advanced use case, but doable none the less.

ofry commented 5 years ago

This can be related: https://github.com/libretro/fmsx-libretro/issues/43

ofry commented 3 years ago

[2021-05-02 22:38:16] that exact proposal is no good. optional exported functions aren't a thing on statically linked platforms, so it'd require changes to every single core to implement the new function. the correct implementation is a function pointer in the struct [2021-05-02 22:39:07] I don't know enough about pc emus to have an informed opinion on the actual functionality, I'll leave that to the relevant core maintainers

ofry commented 3 years ago

These comments taken from Discord, user Alcaro.