Initial take on subsystems support.

This commit is contained in:
Themaister 2014-04-04 14:58:42 +02:00
parent 88526a4ab7
commit c81de5119d
10 changed files with 411 additions and 433 deletions

View File

@ -122,6 +122,9 @@ void autosave_unlock(autosave_t *handle)
void autosave_free(autosave_t *handle)
{
if (!handle)
return;
slock_lock(handle->cond_lock);
handle->quit = true;
slock_unlock(handle->cond_lock);
@ -139,7 +142,7 @@ void autosave_free(autosave_t *handle)
void lock_autosave(void)
{
unsigned i;
for (i = 0; i < ARRAY_SIZE(g_extern.autosave); i++)
for (i = 0; i < g_extern.num_autosave; i++)
{
if (g_extern.autosave[i])
autosave_lock(g_extern.autosave[i]);
@ -149,7 +152,7 @@ void lock_autosave(void)
void unlock_autosave(void)
{
unsigned i;
for (i = 0; i < ARRAY_SIZE(g_extern.autosave); i++)
for (i = 0; i < g_extern.num_autosave; i++)
{
if (g_extern.autosave[i])
autosave_unlock(g_extern.autosave[i]);

View File

@ -185,6 +185,21 @@ void libretro_free_system_info(struct retro_system_info *info)
memset(info, 0, sizeof(*info));
}
const struct retro_game_special_info *libretro_find_subsystem_info(const struct retro_game_special_info *info, unsigned num_info,
const char *ident)
{
unsigned i;
for (i = 0; i < num_info; i++)
{
if (!strcmp(info[i].ident, ident))
return &info[i];
else if (!strcmp(info[i].desc, ident)) // Doesn't hurt
return &info[i];
}
return NULL;
}
static bool find_first_libretro(char *path, size_t size,
const char *dir, const char *rom_path)
{
@ -417,6 +432,7 @@ void uninit_libretro_sym(void)
}
// No longer valid.
free(g_extern.system.special);
memset(&g_extern.system, 0, sizeof(g_extern.system));
#ifdef HAVE_CAMERA
g_extern.camera_active = false;
@ -914,6 +930,23 @@ bool rarch_environment_cb(unsigned cmd, void *data)
return driver_update_system_av_info((const struct retro_system_av_info*)data);
}
case RETRO_ENVIRONMENT_SET_SPECIAL_GAME_TYPES:
{
RARCH_LOG("Environ SET_SPECIAL_GAME_TYPES.\n");
unsigned i;
const struct retro_game_special_info *info = (const struct retro_game_special_info*)data;
for (i = 0; info[i].ident; i++)
RARCH_LOG("Special game type: %s (ident: %s)\n", info[i].desc, info[i].ident);
free(g_extern.system.special);
g_extern.system.special = (struct retro_game_special_info*)calloc(i, sizeof(*g_extern.system.special));
if (!g_extern.system.special)
return false;
memcpy(g_extern.system.special, info, i * sizeof(*g_extern.system.special));
break;
}
case RETRO_ENVIRONMENT_EXEC:
case RETRO_ENVIRONMENT_EXEC_ESCAPE:

View File

@ -64,6 +64,8 @@ void libretro_free_system_info(struct retro_system_info *info);
// Transforms a library id to a name suitable as a pathname.
void libretro_get_current_core_pathname(char *name, size_t size);
const struct retro_game_special_info *libretro_find_subsystem_info(const struct retro_game_special_info *info, unsigned num_info, const char *ident);
extern void (*pretro_init)(void);
extern void (*pretro_deinit)(void);

275
file.c
View File

@ -143,35 +143,8 @@ static ssize_t read_rom_file(const char *path, void **buf)
return ret;
}
static const char *ramtype2str(int type)
{
switch (type)
{
case RETRO_MEMORY_SAVE_RAM:
case RETRO_MEMORY_SNES_GAME_BOY_RAM:
case RETRO_MEMORY_SNES_BSX_RAM:
return ".srm";
case RETRO_MEMORY_RTC:
case RETRO_MEMORY_SNES_GAME_BOY_RTC:
return ".rtc";
case RETRO_MEMORY_SNES_BSX_PRAM:
return ".pram";
case RETRO_MEMORY_SNES_SUFAMI_TURBO_A_RAM:
return ".aram";
case RETRO_MEMORY_SNES_SUFAMI_TURBO_B_RAM:
return ".bram";
default:
return "";
}
}
// Attempt to save valuable RAM data somewhere ...
static void dump_to_file_desperate(const void *data, size_t size, int type)
static void dump_to_file_desperate(const void *data, size_t size, unsigned type)
{
#if defined(_WIN32) && !defined(_XBOX)
const char *base = getenv("APPDATA");
@ -185,14 +158,13 @@ static void dump_to_file_desperate(const void *data, size_t size, int type)
goto error;
char path[PATH_MAX];
snprintf(path, sizeof(path), "%s/RetroArch-recovery-", base);
snprintf(path, sizeof(path), "%s/RetroArch-recovery-%u", base, type);
char timebuf[PATH_MAX];
time_t time_;
time(&time_);
strftime(timebuf, sizeof(timebuf), "%Y-%m-%d-%H-%M-%S", localtime(&time_));
strlcat(path, timebuf, sizeof(path));
strlcat(path, ramtype2str(type), sizeof(path));
if (write_file(path, data, size))
RARCH_WARN("Succeeded in saving RAM data to \"%s\".\n", path);
@ -248,6 +220,7 @@ bool load_state(const char *path)
bool ret = true;
RARCH_LOG("State size: %u bytes.\n", (unsigned)size);
#if 0
void *block_buf[2] = {NULL, NULL};
int block_type[2] = {-1, -1};
size_t block_size[2] = {0};
@ -315,6 +288,8 @@ bool load_state(const char *path)
for (i = 0; i < 2; i++)
if (block_buf[i])
free(block_buf[i]);
#endif
ret = pretro_unserialize(buf, size);
free(buf);
return ret;
@ -362,175 +337,163 @@ void save_ram_file(const char *path, int type)
}
}
#define MAX_ROMS 4
static bool load_roms(unsigned rom_type, const char **rom_paths, size_t roms)
static bool load_roms(const struct retro_game_special_info *special, const struct string_list *roms)
{
size_t i;
unsigned i;
bool ret = true;
if (roms == 0)
struct retro_game_info *info = (struct retro_game_info*)calloc(roms->size, sizeof(*info));
if (!info)
return false;
if (roms > MAX_ROMS)
return false;
void *rom_buf[MAX_ROMS] = {NULL};
long rom_len[MAX_ROMS] = {0};
struct retro_game_info info[MAX_ROMS] = {{NULL}};
if (!g_extern.system.info.need_fullpath)
for (i = 0; i < roms->size; i++)
{
RARCH_LOG("Loading ROM file: %s.\n", rom_paths[0]);
if ((rom_len[0] = read_rom_file(rom_paths[0], &rom_buf[0])) == -1)
const char *path = roms->elems[i].data;
int attr = roms->elems[i].attr.i;
bool need_fullpath = attr & 2;
bool require_rom = attr & 4;
if (require_rom && !*path)
{
RARCH_ERR("Could not read ROM file.\n");
RARCH_LOG("libretro core requires a ROM, but none were provided.\n");
ret = false;
goto end;
}
RARCH_LOG("ROM size: %u bytes.\n", (unsigned)rom_len[0]);
}
else
RARCH_LOG("ROM loading skipped. Implementation will load it on its own.\n");
info[i].path = *path ? path : NULL;
info[0].path = rom_paths[0];
info[0].data = rom_buf[0];
info[0].size = rom_len[0];
info[0].meta = NULL; // Not relevant at this moment.
for (i = 1; i < roms; i++)
{
if (rom_paths[i] &&
!g_extern.system.info.need_fullpath &&
(rom_len[i] = read_file(rom_paths[i], &rom_buf[i])) == -1)
if (!need_fullpath && *path) // Load the ROM into memory.
{
RARCH_ERR("Could not read ROM file: \"%s\".\n", rom_paths[i]);
ret = false;
goto end;
RARCH_LOG("Loading ROM file: %s.\n", path);
// First ROM is significant, attempt to do patching, CRC checking, etc ...
long size = i == 0 ? read_rom_file(path, (void**)&info[i].data) : read_file(path, (void**)&info[i].data);
if (size < 0)
{
RARCH_ERR("Could not read ROM file \"%s\".\n", path);
ret = false;
goto end;
}
info[i].size = size;
}
info[i].path = rom_paths[i];
info[i].data = rom_buf[i];
info[i].size = rom_len[i];
info[i].meta = NULL;
else
RARCH_LOG("ROM loading skipped. Implementation will load it on its own.\n");
}
if (rom_type == 0)
ret = pretro_load_game(&info[0]);
if (special)
ret = pretro_load_game_special(special->id, info, roms->size);
else
ret = pretro_load_game_special(rom_type, info, roms);
ret = pretro_load_game(info);
if (!ret)
RARCH_ERR("Failed to load game.\n");
end:
for (i = 0; i < MAX_ROMS; i++)
free(rom_buf[i]);
for (i = 0; i < roms->size; i++)
free((void*)info[i].data);
free(info);
return ret;
}
static bool load_normal_rom(void)
bool init_rom_file(void)
{
if (g_extern.libretro_no_rom && g_extern.system.no_game)
return pretro_load_game(NULL);
else if (g_extern.libretro_no_rom && !g_extern.system.no_game)
{
RARCH_ERR("No ROM is used, but libretro core does not support this.\n");
unsigned i;
g_extern.temporary_roms = string_list_new();
if (!g_extern.temporary_roms)
return false;
const struct retro_game_special_info *special = NULL;
if (*g_extern.subsystem)
{
special = libretro_find_subsystem_info(g_extern.system.special, g_extern.system.num_special,
g_extern.subsystem);
if (!special)
{
RARCH_ERR("Failed to find subsystem \"%s\" in libretro implementation.\n",
g_extern.subsystem);
return false;
}
if (special->num_roms && !g_extern.subsystem_fullpaths)
{
RARCH_ERR("libretro core requires special ROMs, but none were provided.\n");
return false;
}
else if (special->num_roms && special->num_roms != g_extern.subsystem_fullpaths->size)
{
RARCH_ERR("libretro core requires %u ROMs for subsystem \"%s\", but %u ROMs were provided.\n", special->num_roms, special->desc,
(unsigned)g_extern.subsystem_fullpaths->size);
return false;
}
else if (!special->num_roms && g_extern.subsystem_fullpaths && g_extern.subsystem_fullpaths->size)
{
RARCH_ERR("libretro core takes no ROMs for subsystem \"%s\", but %u ROMs were provided.\n", special->desc,
(unsigned)g_extern.subsystem_fullpaths->size);
return false;
}
}
union string_list_elem_attr attr;
struct string_list *roms = string_list_new();
if (!roms)
return false;
if (*g_extern.subsystem)
{
for (i = 0; i < g_extern.subsystem_fullpaths->size; i++)
{
attr.i = special->roms[i].block_extract;
attr.i |= special->roms[i].need_fullpath << 1;
attr.i |= special->roms[i].required << 2;
string_list_append(roms, g_extern.subsystem_fullpaths->elems[i].data, attr);
}
}
else
{
const char *path = g_extern.fullpath;
return load_roms(0, &path, 1);
attr.i = g_extern.system.info.block_extract;
attr.i |= g_extern.system.info.need_fullpath << 1;
attr.i |= (!g_extern.system.no_game) << 2;
string_list_append(roms, g_extern.fullpath, attr);
}
}
static bool load_sgb_rom(void)
{
const char *path[2] = {
*g_extern.fullpath ? g_extern.fullpath : NULL,
g_extern.gb_rom_path
};
return load_roms(RETRO_GAME_TYPE_SUPER_GAME_BOY, path, 2);
}
static bool load_bsx_rom(bool slotted)
{
const char *path[2] = {
*g_extern.fullpath ? g_extern.fullpath : NULL,
g_extern.bsx_rom_path
};
return load_roms(slotted ? RETRO_GAME_TYPE_BSX_SLOTTED : RETRO_GAME_TYPE_BSX, path, 2);
}
static bool load_sufami_rom(void)
{
const char *path[3] = {
*g_extern.fullpath ? g_extern.fullpath : NULL,
*g_extern.sufami_rom_path[0] ? g_extern.sufami_rom_path[0] : NULL,
*g_extern.sufami_rom_path[1] ? g_extern.sufami_rom_path[1] : NULL,
};
return load_roms(RETRO_GAME_TYPE_SUFAMI_TURBO, path, 3);
}
bool init_rom_file(enum rarch_game_type type)
{
#ifdef HAVE_ZLIB
if (*g_extern.fullpath && !g_extern.system.block_extract)
// Try to extract every ROM we're going to load if appropriate.
for (i = 0; i < roms->size; i++)
{
const char *ext = path_get_extension(g_extern.fullpath);
// block extract check
if (roms->elems[i].attr.i & 1)
continue;
const char *ext = path_get_extension(roms->elems[i].data);
const char *valid_ext = special ?
special->roms[i].valid_extensions :
g_extern.system.info.valid_extensions;
if (ext && !strcasecmp(ext, "zip"))
{
g_extern.rom_file_temporary = true;
if (!zlib_extract_first_rom(g_extern.fullpath, sizeof(g_extern.fullpath), g_extern.system.valid_extensions))
char temporary_rom[PATH_MAX];
strlcpy(temporary_rom, roms->elems[i].data, sizeof(temporary_rom));
if (!zlib_extract_first_rom(temporary_rom, sizeof(temporary_rom), valid_ext))
{
RARCH_ERR("Failed to extract ROM from zipped file: %s.\n", g_extern.fullpath);
g_extern.rom_file_temporary = false;
RARCH_ERR("Failed to extract ROM from zipped file: %s.\n", temporary_rom);
string_list_free(roms);
return false;
}
strlcpy(g_extern.last_rom, g_extern.fullpath, sizeof(g_extern.last_rom));
string_list_append(g_extern.temporary_roms, temporary_rom, attr);
}
}
#endif
switch (type)
{
case RARCH_CART_SGB:
if (!load_sgb_rom())
return false;
break;
case RARCH_CART_NORMAL:
if (!load_normal_rom())
return false;
break;
case RARCH_CART_BSX:
if (!load_bsx_rom(false))
return false;
break;
case RARCH_CART_BSX_SLOTTED:
if (!load_bsx_rom(true))
return false;
break;
case RARCH_CART_SUFAMI:
if (!load_sufami_rom())
return false;
break;
default:
RARCH_ERR("Invalid ROM type.\n");
return false;
}
return true;
// Set attr to need_fullpath as appropriate.
bool ret = load_roms(special, roms);
string_list_free(roms);
return ret;
}

2
file.h
View File

@ -36,7 +36,7 @@ bool save_state(const char *path);
void load_ram_file(const char *path, int type);
void save_ram_file(const char *path, int type);
bool init_rom_file(enum rarch_game_type type);
bool init_rom_file(void);
#ifdef __cplusplus
}

View File

@ -244,12 +244,7 @@ void menu_rom_history_push_current(void)
char tmp[PATH_MAX];
// We loaded a zip, and fullpath points to the extracted file.
// Look at basename instead.
if (g_extern.rom_file_temporary)
snprintf(tmp, sizeof(tmp), "%s.zip", g_extern.basename);
else
strlcpy(tmp, g_extern.fullpath, sizeof(tmp));
strlcpy(tmp, g_extern.fullpath, sizeof(tmp));
if (*tmp)
path_resolve_realpath(tmp, sizeof(tmp));

View File

@ -316,15 +316,6 @@ struct settings
bool core_specific_config;
};
enum rarch_game_type
{
RARCH_CART_NORMAL = 0,
RARCH_CART_SGB,
RARCH_CART_BSX,
RARCH_CART_BSX_SLOTTED,
RARCH_CART_SUFAMI
};
typedef struct rarch_resolution
{
unsigned idx;
@ -358,9 +349,8 @@ struct global
#endif
bool force_fullscreen;
bool rom_file_temporary;
char last_rom[PATH_MAX];
enum rarch_game_type game_type;
struct string_list *temporary_roms;
uint32_t cart_crc;
char gb_rom_path[PATH_MAX];
@ -386,11 +376,15 @@ struct global
char basename[PATH_MAX];
char fullpath[PATH_MAX];
char savefile_name_srm[PATH_MAX];
char savefile_name_rtc[PATH_MAX]; // Make sure that fill_pathname has space.
char savefile_name_psrm[PATH_MAX];
char savefile_name_asrm[PATH_MAX];
char savefile_name_bsrm[PATH_MAX];
// A list of save types and associated paths for all ROMs.
struct string_list *savefiles;
// For --subsystem ROMs.
char subsystem[256];
struct string_list *subsystem_fullpaths;
char savefile_name[PATH_MAX];
char savestate_name[PATH_MAX];
// Used on reentrancy to use a savestate dir.
@ -448,6 +442,9 @@ struct global
retro_usec_t frame_time_last;
core_option_manager_t *core_options;
struct retro_game_special_info *special;
unsigned num_special;
} system;
struct
@ -563,7 +560,8 @@ struct global
unsigned turbo_count;
// Autosave support.
autosave_t *autosave[2];
autosave_t **autosave;
unsigned num_autosave;
// Netplay.
#ifdef HAVE_NETPLAY

View File

@ -162,7 +162,7 @@ extern "C" {
// Regular save ram. This ram is usually found on a game cartridge, backed up by a battery.
// If save game data is too complex for a single memory buffer,
// the SYSTEM_DIRECTORY environment callback can be used.
// the SAVE_DIRECTORY (preferably) or SYSTEM_DIRECTORY environment callback can be used.
#define RETRO_MEMORY_SAVE_RAM 0
// Some games have a built-in clock to keep track of time.
@ -175,21 +175,6 @@ extern "C" {
// Video ram lets a frontend peek into a game systems video RAM (VRAM).
#define RETRO_MEMORY_VIDEO_RAM 3
// Special memory types.
#define RETRO_MEMORY_SNES_BSX_RAM ((1 << 8) | RETRO_MEMORY_SAVE_RAM)
#define RETRO_MEMORY_SNES_BSX_PRAM ((2 << 8) | RETRO_MEMORY_SAVE_RAM)
#define RETRO_MEMORY_SNES_SUFAMI_TURBO_A_RAM ((3 << 8) | RETRO_MEMORY_SAVE_RAM)
#define RETRO_MEMORY_SNES_SUFAMI_TURBO_B_RAM ((4 << 8) | RETRO_MEMORY_SAVE_RAM)
#define RETRO_MEMORY_SNES_GAME_BOY_RAM ((5 << 8) | RETRO_MEMORY_SAVE_RAM)
#define RETRO_MEMORY_SNES_GAME_BOY_RTC ((6 << 8) | RETRO_MEMORY_RTC)
// Special game types passed into retro_load_game_special().
// Only used when multiple ROMs are required.
#define RETRO_GAME_TYPE_BSX 0x101
#define RETRO_GAME_TYPE_BSX_SLOTTED 0x102
#define RETRO_GAME_TYPE_SUFAMI_TURBO 0x103
#define RETRO_GAME_TYPE_SUPER_GAME_BOY 0x104
// Keysyms used for ID in input state callback when polling RETRO_KEYBOARD.
enum retro_key
{
@ -614,9 +599,59 @@ enum retro_mod
// e.g. for cases where the frontend wants to call directly into the core.
//
// If a core wants to expose this interface, SET_PROC_ADDRESS_CALLBACK **MUST** be called from within retro_set_environment().
//
#define RETRO_ENVIRONMENT_SET_SPECIAL_GAME_TYPES 34
// const struct retro_game_special_info * --
// This environment call introduces the concept of libretro "subsystems".
// A subsystem is a variant of a libretro core which supports different kinds of games.
// The purpose of this is to support e.g. emulators which might have special needs, e.g. Super Nintendos Super GameBoy, Sufami Turbo.
// It can also be used to pick among subsystems in an explicit way if the libretro implementation is a multi-system emulator itself.
//
// Loading a game via a subsystem is done with retro_load_game_special(),
// and this environment call allows a libretro core to expose which subsystems are supported for use with retro_load_game_special().
// A core passes an array of retro_game_special_info which is terminated with a zeroed out retro_game_special_info struct.
//
// If a core wants to use this functionality, SET_SPECIAL_GAME_TYPES **MUST** be called from within retro_set_environment().
struct retro_game_special_memory_info
{
const char *extension; // The extension associated with a memory type, e.g. "psram".
unsigned type; // The memory type for retro_get_memory(). This should be at least 0x100 to avoid conflict with standardized libretro memory types.
};
struct retro_game_special_rom_info
{
const char *desc; // Describes what the ROM is (SGB bios, GB rom, etc).
const char *ident; // A computer friendly short string identifier for the ROM type. Must be [a-z].
const char *valid_extensions; // Same definition as retro_get_system_info().
bool need_fullpath; // Same definition as retro_get_system_info().
bool block_extract; // Same definition as retro_get_system_info().
bool required; // This is set if the ROM is required to load a game. If this is set to false, a zeroed-out retro_game_info can be passed.
// ROMs can have multiple associated persistent memory types (retro_get_memory()).
const struct retro_game_special_memory_info *memory;
unsigned num_memory;
};
struct retro_game_special_info
{
const char *desc; // Human-readable string of the subsystem type, e.g. "Super GameBoy"
// A computer friendly short string identifier for the subsystem type.
// This name must be [a-z].
// E.g. if desc is "Super GameBoy", this can be "sgb".
// This identifier can be used for command-line interfaces, etc.
const char *ident;
// Infos for each ROM. The first entry is assumed to be the "most significant" ROM for frontend purposes.
// E.g. with Super GameBoy, the first ROM should be the GameBoy ROM, as it is the most "significant" ROM to a user.
// If a frontend creates new file paths based on the ROM used (e.g. savestates), it should use the path for the first ROM to do so.
const struct retro_game_special_rom_info *roms;
unsigned num_roms; // Number of ROMs associated with a subsystem.
unsigned id; // The type passed to retro_load_game_special().
};
typedef void (*retro_proc_address_t)(void);
// libretro API extension functions:
// (None here so far).
//////

View File

@ -836,19 +836,53 @@ static void set_basename(const char *path)
*dst = '\0';
}
static void set_special_paths(char **argv, unsigned roms)
{
unsigned i;
// First ROM is the significant one.
set_basename(argv[0]);
g_extern.subsystem_fullpaths = string_list_new();
rarch_assert(g_extern.subsystem_fullpaths);
union string_list_elem_attr attr;
attr.i = 0;
for (i = 0; i < roms; i++)
string_list_append(g_extern.subsystem_fullpaths, argv[i], attr);
// We defer SRAM path updates until we can resolve it.
// It is more complicated for special game types.
if (!g_extern.has_set_state_path)
fill_pathname_noext(g_extern.savestate_name, g_extern.basename, ".state", sizeof(g_extern.savestate_name));
if (path_is_directory(g_extern.savestate_name))
{
fill_pathname_dir(g_extern.savestate_name, g_extern.basename, ".state", sizeof(g_extern.savestate_name));
RARCH_LOG("Redirecting save state to \"%s\".\n", g_extern.savestate_name);
}
// If this is already set,
// do not overwrite it as this was initialized before in a menu or otherwise.
if (!*g_settings.system_directory)
fill_pathname_basedir(g_settings.system_directory, argv[0], sizeof(g_settings.system_directory));
}
static void set_paths(const char *path)
{
set_basename(path);
if (!g_extern.has_set_save_path)
fill_pathname_noext(g_extern.savefile_name_srm, g_extern.basename, ".srm", sizeof(g_extern.savefile_name_srm));
fill_pathname_noext(g_extern.savefile_name, g_extern.basename, ".srm", sizeof(g_extern.savefile_name));
if (!g_extern.has_set_state_path)
fill_pathname_noext(g_extern.savestate_name, g_extern.basename, ".state", sizeof(g_extern.savestate_name));
if (path_is_directory(g_extern.savefile_name_srm))
if (path_is_directory(g_extern.savefile_name))
{
fill_pathname_dir(g_extern.savefile_name_srm, g_extern.basename, ".srm", sizeof(g_extern.savefile_name_srm));
RARCH_LOG("Redirecting save file to \"%s\".\n", g_extern.savefile_name_srm);
fill_pathname_dir(g_extern.savefile_name, g_extern.basename, ".srm", sizeof(g_extern.savefile_name));
RARCH_LOG("Redirecting save file to \"%s\".\n", g_extern.savefile_name);
}
if (path_is_directory(g_extern.savestate_name))
{
@ -895,7 +929,6 @@ static void parse_input(int argc, char *argv[])
{ "size", 1, &val, 's' },
#endif
{ "verbose", 0, NULL, 'v' },
{ "gameboy", 1, NULL, 'g' },
{ "config", 1, NULL, 'c' },
{ "appendconfig", 1, &val, 'C' },
{ "mouse", 1, NULL, 'm' },
@ -905,11 +938,7 @@ static void parse_input(int argc, char *argv[])
{ "justifiers", 0, NULL, 'J' },
{ "dualanalog", 1, NULL, 'A' },
{ "savestate", 1, NULL, 'S' },
{ "bsx", 1, NULL, 'b' },
{ "bsxslot", 1, NULL, 'B' },
{ "multitap", 0, NULL, '4' },
{ "sufamiA", 1, NULL, 'Y' },
{ "sufamiB", 1, NULL, 'Z' },
#ifdef HAVE_BSV_MOVIE
{ "bsvplay", 1, NULL, 'P' },
{ "bsvrecord", 1, NULL, 'R' },
@ -932,6 +961,7 @@ static void parse_input(int argc, char *argv[])
{ "no-patch", 0, &val, 'n' },
{ "detach", 0, NULL, 'D' },
{ "features", 0, &val, 'f' },
{ "subsystem", 1, NULL, 'Z' },
{ NULL, 0, NULL, 0 }
};
@ -959,7 +989,7 @@ static void parse_input(int argc, char *argv[])
#define BSV_MOVIE_ARG
#endif
const char *optstring = "hs:fvS:m:p4jJA:g:b:c:B:Y:Z:U:DN:" BSV_MOVIE_ARG NETPLAY_ARG DYNAMIC_ARG FFMPEG_RECORD_ARG;
const char *optstring = "hs:fvS:m:p4jJA:c:U:DN:" BSV_MOVIE_ARG NETPLAY_ARG DYNAMIC_ARG FFMPEG_RECORD_ARG;
for (;;)
{
@ -970,7 +1000,6 @@ static void parse_input(int argc, char *argv[])
if (c == -1)
break;
switch (c)
{
case 'h':
@ -992,6 +1021,10 @@ static void parse_input(int argc, char *argv[])
g_extern.has_set_libretro_device[1] = true;
break;
case 'Z':
strlcpy(g_extern.subsystem, optarg, sizeof(g_extern.subsystem));
break;
case 'A':
port = strtol(optarg, NULL, 0);
if (port < 1 || port > MAX_PLAYERS)
@ -1005,7 +1038,7 @@ static void parse_input(int argc, char *argv[])
break;
case 's':
strlcpy(g_extern.savefile_name_srm, optarg, sizeof(g_extern.savefile_name_srm));
strlcpy(g_extern.savefile_name, optarg, sizeof(g_extern.savefile_name));
g_extern.has_set_save_path = true;
break;
@ -1013,31 +1046,6 @@ static void parse_input(int argc, char *argv[])
g_extern.force_fullscreen = true;
break;
case 'g':
strlcpy(g_extern.gb_rom_path, optarg, sizeof(g_extern.gb_rom_path));
g_extern.game_type = RARCH_CART_SGB;
break;
case 'b':
strlcpy(g_extern.bsx_rom_path, optarg, sizeof(g_extern.bsx_rom_path));
g_extern.game_type = RARCH_CART_BSX;
break;
case 'B':
strlcpy(g_extern.bsx_rom_path, optarg, sizeof(g_extern.bsx_rom_path));
g_extern.game_type = RARCH_CART_BSX_SLOTTED;
break;
case 'Y':
strlcpy(g_extern.sufami_rom_path[0], optarg, sizeof(g_extern.sufami_rom_path[0]));
g_extern.game_type = RARCH_CART_SUFAMI;
break;
case 'Z':
strlcpy(g_extern.sufami_rom_path[1], optarg, sizeof(g_extern.sufami_rom_path[1]));
g_extern.game_type = RARCH_CART_SUFAMI;
break;
case 'S':
strlcpy(g_extern.savestate_name, optarg, sizeof(g_extern.savestate_name));
g_extern.has_set_state_path = true;
@ -1239,14 +1247,16 @@ static void parse_input(int argc, char *argv[])
rarch_fail(1, "parse_input()");
}
}
else if (optind < argc)
else if (!*g_extern.subsystem && optind < argc)
set_paths(argv[optind]);
else if (*g_extern.subsystem && optind < argc)
set_special_paths(argv + optind, argc - optind);
else
g_extern.libretro_no_rom = true;
// Copy SRM/state dirs used, so they can be reused on reentrancy.
if (g_extern.has_set_save_path && path_is_directory(g_extern.savefile_name_srm))
strlcpy(g_extern.savefile_dir, g_extern.savefile_name_srm, sizeof(g_extern.savefile_dir));
if (g_extern.has_set_save_path && path_is_directory(g_extern.savefile_name))
strlcpy(g_extern.savefile_dir, g_extern.savefile_name, sizeof(g_extern.savefile_dir));
if (g_extern.has_set_state_path && path_is_directory(g_extern.savestate_name))
strlcpy(g_extern.savestate_dir, g_extern.savestate_name, sizeof(g_extern.savestate_dir));
}
@ -1302,73 +1312,26 @@ static void init_controllers(void)
static inline void load_save_files(void)
{
switch (g_extern.game_type)
{
case RARCH_CART_NORMAL:
load_ram_file(g_extern.savefile_name_srm, RETRO_MEMORY_SAVE_RAM);
load_ram_file(g_extern.savefile_name_rtc, RETRO_MEMORY_RTC);
break;
unsigned i;
if (!g_extern.savefiles)
return;
case RARCH_CART_SGB:
load_ram_file(g_extern.savefile_name_srm, RETRO_MEMORY_SNES_GAME_BOY_RAM);
load_ram_file(g_extern.savefile_name_rtc, RETRO_MEMORY_SNES_GAME_BOY_RTC);
break;
case RARCH_CART_BSX:
case RARCH_CART_BSX_SLOTTED:
load_ram_file(g_extern.savefile_name_srm, RETRO_MEMORY_SNES_BSX_RAM);
load_ram_file(g_extern.savefile_name_psrm, RETRO_MEMORY_SNES_BSX_PRAM);
break;
case RARCH_CART_SUFAMI:
load_ram_file(g_extern.savefile_name_asrm, RETRO_MEMORY_SNES_SUFAMI_TURBO_A_RAM);
load_ram_file(g_extern.savefile_name_bsrm, RETRO_MEMORY_SNES_SUFAMI_TURBO_B_RAM);
break;
default:
break;
}
for (i = 0; i < g_extern.savefiles->size; i++)
load_ram_file(g_extern.savefiles->elems[i].data, g_extern.savefiles->elems[i].attr.i);
}
static inline void save_files(void)
{
switch (g_extern.game_type)
unsigned i;
if (!g_extern.savefiles)
return;
for (i = 0; i < g_extern.savefiles->size; i++)
{
case RARCH_CART_NORMAL:
RARCH_LOG("Saving regular SRAM.\n");
RARCH_LOG("SRM: %s\n", g_extern.savefile_name_srm);
RARCH_LOG("RTC: %s\n", g_extern.savefile_name_rtc);
save_ram_file(g_extern.savefile_name_srm, RETRO_MEMORY_SAVE_RAM);
save_ram_file(g_extern.savefile_name_rtc, RETRO_MEMORY_RTC);
break;
case RARCH_CART_SGB:
RARCH_LOG("Saving Gameboy SRAM.\n");
RARCH_LOG("SRM: %s\n", g_extern.savefile_name_srm);
RARCH_LOG("RTC: %s\n", g_extern.savefile_name_rtc);
save_ram_file(g_extern.savefile_name_srm, RETRO_MEMORY_SNES_GAME_BOY_RAM);
save_ram_file(g_extern.savefile_name_rtc, RETRO_MEMORY_SNES_GAME_BOY_RTC);
break;
case RARCH_CART_BSX:
case RARCH_CART_BSX_SLOTTED:
RARCH_LOG("Saving BSX (P)RAM.\n");
RARCH_LOG("SRM: %s\n", g_extern.savefile_name_srm);
RARCH_LOG("PSRM: %s\n", g_extern.savefile_name_psrm);
save_ram_file(g_extern.savefile_name_srm, RETRO_MEMORY_SNES_BSX_RAM);
save_ram_file(g_extern.savefile_name_psrm, RETRO_MEMORY_SNES_BSX_PRAM);
break;
case RARCH_CART_SUFAMI:
RARCH_LOG("Saving Sufami turbo A/B RAM.\n");
RARCH_LOG("ASRM: %s\n", g_extern.savefile_name_asrm);
RARCH_LOG("BSRM: %s\n", g_extern.savefile_name_bsrm);
save_ram_file(g_extern.savefile_name_asrm, RETRO_MEMORY_SNES_SUFAMI_TURBO_A_RAM);
save_ram_file(g_extern.savefile_name_bsrm, RETRO_MEMORY_SNES_SUFAMI_TURBO_B_RAM);
break;
default:
break;
unsigned type = g_extern.savefiles->elems[i].attr.i;
const char *path = g_extern.savefiles->elems[i].data;
RARCH_LOG("Saving RAM type #%u to \"%s\".\n", type, path);
save_ram_file(path, type);
}
}
@ -1727,54 +1690,29 @@ static void init_libretro_cbs(void)
#if defined(HAVE_THREADS)
void rarch_init_autosave(void)
{
int ram_types[2] = {-1, -1};
const char *ram_paths[2] = {NULL, NULL};
if (g_settings.autosave_interval < 1 || !g_extern.savefiles)
return;
switch (g_extern.game_type)
g_extern.autosave = (autosave_t**)calloc(g_extern.savefiles->size, sizeof(*g_extern.autosave));
if (!g_extern.autosave)
return;
g_extern.num_autosave = g_extern.savefiles->size;
unsigned i;
for (i = 0; i < g_extern.savefiles->size; i++)
{
case RARCH_CART_BSX:
case RARCH_CART_BSX_SLOTTED:
ram_types[0] = RETRO_MEMORY_SNES_BSX_RAM;
ram_types[1] = RETRO_MEMORY_SNES_BSX_PRAM;
ram_paths[0] = g_extern.savefile_name_srm;
ram_paths[1] = g_extern.savefile_name_psrm;
break;
const char *path = g_extern.savefiles->elems[i].data;
unsigned type = g_extern.savefiles->elems[i].attr.i;
case RARCH_CART_SUFAMI:
ram_types[0] = RETRO_MEMORY_SNES_SUFAMI_TURBO_A_RAM;
ram_types[1] = RETRO_MEMORY_SNES_SUFAMI_TURBO_B_RAM;
ram_paths[0] = g_extern.savefile_name_asrm;
ram_paths[1] = g_extern.savefile_name_bsrm;
break;
case RARCH_CART_SGB:
ram_types[0] = RETRO_MEMORY_SNES_GAME_BOY_RAM;
ram_types[1] = RETRO_MEMORY_SNES_GAME_BOY_RTC;
ram_paths[0] = g_extern.savefile_name_srm;
ram_paths[1] = g_extern.savefile_name_rtc;
break;
default:
ram_types[0] = RETRO_MEMORY_SAVE_RAM;
ram_types[1] = RETRO_MEMORY_RTC;
ram_paths[0] = g_extern.savefile_name_srm;
ram_paths[1] = g_extern.savefile_name_rtc;
}
if (g_settings.autosave_interval > 0)
{
unsigned i;
for (i = 0; i < sizeof(g_extern.autosave) / sizeof(g_extern.autosave[0]); i++)
if (pretro_get_memory_size(type) > 0)
{
if (ram_paths[i] && *ram_paths[i] && pretro_get_memory_size(ram_types[i]) > 0)
{
g_extern.autosave[i] = autosave_new(ram_paths[i],
pretro_get_memory_data(ram_types[i]),
pretro_get_memory_size(ram_types[i]),
g_settings.autosave_interval);
if (!g_extern.autosave[i])
RARCH_WARN("Could not initialize autosave.\n");
}
g_extern.autosave[i] = autosave_new(path,
pretro_get_memory_data(type),
pretro_get_memory_size(type),
g_settings.autosave_interval);
if (!g_extern.autosave[i])
RARCH_WARN("Could not initialize autosave.\n");
}
}
}
@ -1782,12 +1720,10 @@ void rarch_init_autosave(void)
void rarch_deinit_autosave(void)
{
unsigned i;
for (i = 0; i < ARRAY_SIZE(g_extern.autosave); i++)
{
if (g_extern.autosave[i])
autosave_free(g_extern.autosave[i]);
g_extern.autosave[i] = NULL;
}
for (i = 0; i < g_extern.num_autosave; i++)
autosave_free(g_extern.autosave[i]);
free(g_extern.autosave);
g_extern.num_autosave = 0;
}
#endif
@ -1838,84 +1774,85 @@ static void set_savestate_auto_index(void)
static void fill_pathnames(void)
{
switch (g_extern.game_type)
string_list_free(g_extern.savefiles);
g_extern.savefiles = string_list_new();
rarch_assert(g_extern.savefiles);
// For subsystems, we know exactly which RAM types are supported.
if (*g_extern.subsystem)
{
case RARCH_CART_BSX:
case RARCH_CART_BSX_SLOTTED:
// BSX PSRM
if (!g_extern.has_set_save_path)
unsigned i;
const struct retro_game_special_info *info = libretro_find_subsystem_info(g_extern.system.special, g_extern.system.num_special, g_extern.subsystem);
// We'll handle this error gracefully later.
unsigned num_roms = info ? info->num_roms : 0;
bool use_sram_dir = path_is_directory(g_extern.savefile_name);
for (i = 0; i < num_roms; i++)
{
unsigned j;
for (j = 0; j < info->roms[i].num_memory; j++)
{
fill_pathname(g_extern.savefile_name_srm,
g_extern.bsx_rom_path, ".srm", sizeof(g_extern.savefile_name_srm));
const struct retro_game_special_memory_info *mem = &info->roms[i].memory[j];
union string_list_elem_attr attr;
char path[PATH_MAX];
char ext[32];
snprintf(ext, sizeof(ext), ".%s", mem->extension);
if (use_sram_dir)
{
// Redirect ROM fullpath to save directory.
strlcpy(path, g_extern.savefile_name, sizeof(path));
fill_pathname_dir(path, g_extern.subsystem_fullpaths->elems[i].data, ext,
sizeof(path));
}
else
{
fill_pathname(path, g_extern.subsystem_fullpaths->elems[i].data,
ext, sizeof(path));
}
attr.i = mem->type;
string_list_append(g_extern.savefiles, path, attr);
}
}
fill_pathname(g_extern.savefile_name_psrm,
g_extern.savefile_name_srm, ".psrm", sizeof(g_extern.savefile_name_psrm));
// Let other relevant paths be inferred from the main SRAM location.
if (!g_extern.has_set_save_path)
fill_pathname_noext(g_extern.savefile_name, g_extern.basename, ".srm", sizeof(g_extern.savefile_name));
if (path_is_directory(g_extern.savefile_name))
{
fill_pathname_dir(g_extern.savefile_name, g_extern.basename, ".srm", sizeof(g_extern.savefile_name));
RARCH_LOG("Redirecting save file to \"%s\".\n", g_extern.savefile_name);
}
}
else
{
union string_list_elem_attr attr;
attr.i = RETRO_MEMORY_SAVE_RAM;
string_list_append(g_extern.savefiles, g_extern.savefile_name, attr);
if (!g_extern.has_set_state_path)
{
fill_pathname(g_extern.savestate_name,
g_extern.bsx_rom_path, ".state", sizeof(g_extern.savestate_name));
}
break;
case RARCH_CART_SUFAMI:
if (g_extern.has_set_save_path && *g_extern.sufami_rom_path[0] && *g_extern.sufami_rom_path[1])
RARCH_WARN("Sufami Turbo SRAM paths will be inferred from their respective paths to avoid conflicts.\n");
// SUFAMI ARAM
fill_pathname(g_extern.savefile_name_asrm,
g_extern.sufami_rom_path[0], ".srm", sizeof(g_extern.savefile_name_asrm));
// SUFAMI BRAM
fill_pathname(g_extern.savefile_name_bsrm,
g_extern.sufami_rom_path[1], ".srm", sizeof(g_extern.savefile_name_bsrm));
if (!g_extern.has_set_state_path)
{
fill_pathname(g_extern.savestate_name,
*g_extern.sufami_rom_path[0] ?
g_extern.sufami_rom_path[0] : g_extern.sufami_rom_path[1],
".state", sizeof(g_extern.savestate_name));
}
break;
case RARCH_CART_SGB:
if (!g_extern.has_set_save_path)
{
fill_pathname(g_extern.savefile_name_srm,
g_extern.gb_rom_path, ".srm", sizeof(g_extern.savefile_name_srm));
}
if (!g_extern.has_set_state_path)
{
fill_pathname(g_extern.savestate_name,
g_extern.gb_rom_path, ".state", sizeof(g_extern.savestate_name));
}
fill_pathname(g_extern.savefile_name_rtc,
g_extern.savefile_name_srm, ".rtc", sizeof(g_extern.savefile_name_rtc));
break;
default:
// Infer .rtc save path from save ram path.
fill_pathname(g_extern.savefile_name_rtc,
g_extern.savefile_name_srm, ".rtc", sizeof(g_extern.savefile_name_rtc));
// Infer .rtc save path from save ram path.
char savefile_name_rtc[PATH_MAX];
attr.i = RETRO_MEMORY_RTC;
fill_pathname(savefile_name_rtc,
g_extern.savefile_name, ".rtc", sizeof(savefile_name_rtc));
string_list_append(g_extern.savefiles, savefile_name_rtc, attr);
}
#ifdef HAVE_BSV_MOVIE
fill_pathname(g_extern.bsv.movie_path, g_extern.savefile_name_srm, "", sizeof(g_extern.bsv.movie_path));
fill_pathname(g_extern.bsv.movie_path, g_extern.savefile_name, "", sizeof(g_extern.bsv.movie_path));
#endif
if (*g_extern.basename)
{
if (!(*g_extern.ups_name))
if (!*g_extern.ups_name)
fill_pathname_noext(g_extern.ups_name, g_extern.basename, ".ups", sizeof(g_extern.ups_name));
if (!(*g_extern.bps_name))
if (!*g_extern.bps_name)
fill_pathname_noext(g_extern.bps_name, g_extern.basename, ".bps", sizeof(g_extern.bps_name));
if (!(*g_extern.ips_name))
if (!*g_extern.ips_name)
fill_pathname_noext(g_extern.ips_name, g_extern.basename, ".ips", sizeof(g_extern.ips_name));
}
}
@ -2530,19 +2467,22 @@ void rarch_disk_control_append_image(const char *path)
rarch_deinit_autosave();
#endif
// Update paths for our new image.
// If we actually use append_image,
// we assume that we started out in a single disk case,
// and that this way of doing it makes the most sense.
set_paths(path);
fill_pathnames();
// TODO: Need to figure out what to do with subsystems case.
if (!*g_extern.subsystem)
{
// Update paths for our new image.
// If we actually use append_image,
// we assume that we started out in a single disk case,
// and that this way of doing it makes the most sense.
set_paths(path);
fill_pathnames();
}
#if defined(HAVE_THREADS)
rarch_init_autosave();
#endif
rarch_disk_control_set_eject(false, false);
}
void rarch_disk_control_set_eject(bool new_state, bool log)
@ -2874,7 +2814,6 @@ static void init_state(void)
{
g_extern.video_active = true;
g_extern.audio_active = true;
g_extern.game_type = RARCH_CART_NORMAL;
}
static void init_state_first(void)
@ -3007,19 +2946,18 @@ int rarch_main_init(int argc, char *argv[])
if (g_extern.libretro_no_rom && !g_extern.libretro_dummy)
{
if (!init_rom_file(g_extern.game_type))
if (!init_rom_file())
goto error;
}
else if (!g_extern.libretro_dummy)
{
fill_pathnames();
if (!init_rom_file(g_extern.game_type))
if (!init_rom_file())
goto error;
set_savestate_auto_index();
if (!g_extern.sram_load_disable)
load_save_files();
else
@ -3278,13 +3216,24 @@ void rarch_main_deinit(void)
pretro_deinit();
uninit_libretro_sym();
if (g_extern.rom_file_temporary)
if (g_extern.temporary_roms)
{
RARCH_LOG("Removing temporary ROM file: %s.\n", g_extern.last_rom);
if (remove(g_extern.last_rom) < 0)
RARCH_ERR("Failed to remove temporary file: %s.\n", g_extern.last_rom);
g_extern.rom_file_temporary = false;
unsigned i;
for (i = 0; i < g_extern.temporary_roms->size; i++)
{
const char *path = g_extern.temporary_roms->elems[i].data;
RARCH_LOG("Removing temporary ROM file: %s.\n", path);
if (remove(path) < 0)
RARCH_ERR("Failed to remove temporary file: %s.\n", path);
}
}
string_list_free(g_extern.temporary_roms);
g_extern.temporary_roms = NULL;
string_list_free(g_extern.subsystem_fullpaths);
string_list_free(g_extern.savefiles);
g_extern.subsystem_fullpaths = NULL;
g_extern.savefiles = NULL;
g_extern.main_is_init = false;
}

View File

@ -1009,8 +1009,8 @@ bool config_load_file(const char *path, bool set_defaults)
else if (path_is_directory(tmp_str))
{
strlcpy(g_extern.savefile_dir, tmp_str, sizeof(g_extern.savefile_dir));
strlcpy(g_extern.savefile_name_srm, tmp_str, sizeof(g_extern.savefile_name_srm));
fill_pathname_dir(g_extern.savefile_name_srm, g_extern.basename, ".srm", sizeof(g_extern.savefile_name_srm));
strlcpy(g_extern.savefile_name, tmp_str, sizeof(g_extern.savefile_name));
fill_pathname_dir(g_extern.savefile_name, g_extern.basename, ".srm", sizeof(g_extern.savefile_name));
}
else
RARCH_WARN("savefile_directory is not a directory, ignoring ...\n");