libretro / RetroArch

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

Need for a standard method to load special content #620

Closed andres-asm closed 10 years ago

andres-asm commented 10 years ago

It would be great to have a standard method to load special content. I have a few ideas but I'm not sure how to implement them, I thought I'd mention my ideas here before even trying.

I think the best way would be to follow core options, I started by defining this:

in libretro.h:

struct retro_game_special_parameter
{
   const char *name;             // Parameter name.
   const char *options;          // Possible options 
   const char *option_value; // Selected option from options if options was not NULL.
   bool is_file;                        // Is the variable a filename?
   const char *value              // Filename 
   bool is_apendable;            // Defines if there can be more than one of this variable
};

#define RETRO_ENVIRONMENT_GET_PARAMETERS 33
#define RETRO_ENVIRONMENT_SET_PARAMETERS 34

For instance mess would return an array of parameters to the frontend, something like this:

[
   "SYSTEM", "NES|FAMICOM|SNES|JAGUAR" ,NULL, FALSE, NULL, FALSE,
   "MEDIA", "CART|FLOPPY|ROM|ISO", NULL ,TRUE, NULL,TRUE,
]

For bsnes

[
   "SYSTEM", "SGB|SUFAMI" ,NULL, FALSE, NULL, FALSE,
   "FILE1", "NULL", NULL ,TRUE, NULL,FALSE,
   "FILE2", "NULL", NULL ,TRUE, NULL,FALSE,
]

The frontend then would have to populate a menu similar to the shader menu, where you can select from options on some entries and choose a filename on others.

Then when loading the game instead of passing retro_game_info, frontend would pass the list retro_game_special_parameter and the core would have to figure what to do with the parameters, something like this:

For a single disk FDS game in MESS:

[
   "SYSTEM", "NES|FAMICOM|SNES|JAGUAR" ,"FAMICOM", FALSE, NULL, FALSE,
   "MEDIA", "CART|FLOPPY|ROM|ISO", "FLOPPY", TRUE,"SMB2.FDS" ,TRUE,
]

For a multi disk FDS game in MESS (I don't know if such a thing exists):

[
   "SYSTEM", "NES|FAMICOM|SNES|JAGUAR" ,"FAMICOM", FALSE, NULL, FALSE,
   "MEDIA", "CART|FLOPPY|ROM|ISO", "FLOPPY", TRUE,"SMB2_1.FDS" ,TRUE,
   "MEDIA", "CART|FLOPPY|ROM|ISO", "FLOPPY", TRUE,"SMB2_2.FDS" ,TRUE,
]

For a SGB game in BSNES

[
   "SYSTEM", "SGB|SUFAMI" ,SGB, FALSE, NULL, FALSE,
   "FILE1", "NULL", NULL ,TRUE, "sgb.rom",FALSE,
   "FILE2", "NULL", NULL ,TRUE, "wario.gb",FALSE,
]

For a SUFAMI game in TGB

[
   "SYSTEM", "SGB|SUFAMI" ,SUFAMI, FALSE, NULL, FALSE,
   "FILE1", "NULL", NULL ,TRUE, "pokemona.gb",FALSE,
   "FILE2", "NULL", NULL ,TRUE, "pokemonb.gb",FALSE,
]

Anyway, it's just my idea on how it could work, I understand how variable works but I don't think I know enough to be able to implement something similar core_options.c to populate and return this parameters

Themaister commented 10 years ago

So, we have retro_load_game_special(). It takes:

retro_load_game_special(unsigned game_type, const struct retro_game_info *info, size_t num_info);

The "type" of special ROM (Sufami Turbo, SGB, etc) is set in game_type and multiple ROMs are passed in info/num_info. The thing however is that we don't know which types of game_type we can pass it, and the number of roms which is the problem here.

So, the proposed interface must pass a list of supported ROM types, and for each ROM type, we have to pass descriptors for what we want the frontend to put in the various info structs.

#define RETRO_ENVIRONMENT_GET_PARAMETERS 33

doesn't sound quite right, maybe RETRO_ENVIORNMENT_SET_SPECIAL_GAME_TYPES? Doubt we need a GET version of that. This is something the core signals to the frontend at beginning.

struct retro_game_special_rom_info
{
    const char *desc; // Describes what the ROM is (SGB bios, GB rom, etc).
    const char *valid_extensions; // Same as get_system_info.
    bool need_fullpath; // Same as get_system_info.
    bool required; // If the ROM is required to load a game. Will probably be useful. if not loaded, just pass a blanked out retro_game_info ...
};

struct retro_game_special_info
{
    const char *desc; // Human-readable string of the rom type, e.g. "Super GameBoy"
    unsigned id; // The ID we pass to retro_load_game_special().
    size_t num_info; // Number of ROM infos we have to pass. 
    const struct retro_game_special_rom_info *infos; // Infos for each ROM.
};

And you could call:

struct retro_game_special_info special[] = {
    { "Sufami Turbo", 0, 3, ... },
    { "Super GameBoy", 1, 2, ... },
    { NULL }, // Terminate array
};
environ_cb(RETRO_ENVIRONMENT_SET_SPECIAL_GAME_TYPES, special);

Think that can work for MESS?

andres-asm commented 10 years ago

MESS needs to know the media type and the system type, many games are just .rom inside zip files.

Maybe alongside with the human readable descriptions we could have these optional parameters? it would map perfectly to your structure, MESS system is needed only once and would be part of retro_game_special_info and MESS media type is needed per ROM so it would be part of retro_game_special_rom_info.

We could add the options the core expects to retro_system_info. You told me the core is loaded when selected just lo fetch some information. We could populate the options at that point, then RetroArch would have the required info to build a menu just for that core.

BSNES:

info->special_info_options = "SGB|SUFAMI"
info->special_rom_info_options = NULL

MESS

info->special_info_options = "FAMICOM|NES|JAGUAR|........ETC"
info->special_rom_info_options = "CART|ROM|CHD|...ETC"
Themaister commented 10 years ago

I don't get what special_info_options and special_rom_info_options are supposed to do there at all. What would optional parameters mean in this case? MESS really needs to have tons of options + system to be able to load a simple ROM?

Enumerating all the different sub-systems (famicom/nes/jaguar, etc) can be done with an array at least.

andres-asm commented 10 years ago

Yes mess/ume needs that at least. System type once and media type for each entry. They have a new method with XML definitions or something but that needs a different romset.

Themaister commented 10 years ago

Well, the idea is that the core lays out exactly which order ROMs should be passed in (e.g. ROM #1 = so-and-so, ROM #2 = such-and-such). Don't see why you need to specify the "media type" per ROM when the core already knows (?) ... There seems to be a 1:1 mapping for system to rom type. If that doesn't work then this is simply hilariously complex and stupid. Do you really have to do this to even use MESS to begin with?

Anyways, if MESS provides a way to avoid all this complexity with newer rom-sets, why force the pain onto the libretro/frontend? Seems like a bad idea to completely reinvent the wheel. Not that I don't want to repurpose retro_load_game_special(), but bolting on tons of cruft to support an awkward odd-ball case is probably not a good idea.

I think there's something I'm not quite getting yet with MESS though. Can you explain in more detail how you load a ROM in MESS atm?

andres-asm commented 10 years ago

About softlists, it's still not a method widely used but you are right, and I'm looking on a way to support it.

And yeah mostly mapping is 1:1 but it's not always the case.

MESS/MAME are shallow forks and they are mostly working the same way you'd load them from a command line, I dunno if that approach is correct but it was like that when I started messing with it.

Basically now we pass an array of parameters to MAME OSD. Parameters are mostly the same between MAME, MESS, the only real differences are in their ROM loading parameters.

You load a MAME game by passing game.zip You load a MESS game by passing SYSTEMNAME, MEDIATYPE, game.zip

I'm getting SYSTEMNAME from the PATH, the penultimate entry in the path to the game is being assumed as SYSTEMNAME, since most romsets are categorized like that it just works.

I'm getting MEDIATYPE from core options.

Right now I'm adding an auto option for MEDIATYPE, since most common systems have a 1:1 mapping with their media it show be good for most single file stuff.

I think moving forward with your idea is the best, and then adapting the CORE to follow that paradigm.

Themaister commented 10 years ago

Ok, if mediatype can be inferred almost all the time, it's probably best to leave it as a core option (with auto as default or explicit for weird cases). For system selection, retro_load_game() can have the "auto" semantic it has now I guess. The "load special" approach would just allow you to explicitly select the system rather than relying on inferring path. That sounds ok?

andres-asm commented 10 years ago

Yes! That would work well for SUFAMI/SGB too I guess.

BTW. I just added softlist support to mess-libretro, so this would only be required for people who don't want to move to the new format (I can still find the new format romset and the old format on most trackers)

Since MESS/MAME is so big I guess most users will just update their romsets accordingly.

Themaister commented 10 years ago

Yes, the goal is to replace the SNES-specific cruft still lingering in libretro. Next up after that is cleaning up the controller types. Some SNES specifics still there as well, but we can take a similar SET_CONTROLLER_TYPES kind of approach, (or retro_get_controller_types() with the new GET_PROC_ADDRESS interface) so it's fine.

I'll implement the CLI side of multi-ROM loading + libretro interfacing when I have time.

andres-asm commented 10 years ago

Thanks!

Themaister commented 10 years ago

I've started to work on this now.

Themaister commented 10 years ago

Solved in pull request.