Merge pull request #636 from libretro/subsystem

libretro subsystem API
This commit is contained in:
Twinaphex 2014-04-04 22:46:26 +02:00
commit f604d7e377
17 changed files with 512 additions and 514 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

@ -8,7 +8,7 @@ retroarch \- The reference frontend for the libretro API.
.SH SYNOPSIS
\fBretroarch\fR [rom file] [OPTIONS]...
\fBretroarch\fR [rom file(s)] [OPTIONS]...
.SH "DESCRIPTION"
@ -117,28 +117,11 @@ Multiple config files are delimited by ','.
Every config file will be appended in order where the key-value pairs of the next config file takes priority over the old ones.
.TP
\fB--gameboy PATH, -g PATH\fR
Path to a Nintendo Game Boy ROM. If this flag is set, the Super Game Boy subsystem will be activated. The Super Game Boy BIOS needs to be loaded as the normal rom.
.TP
\fB--bsx PATH, -b PATH\fR
Path to BS-X rom. Load BS-X BIOS as the regular rom.
When using BS-X, save ram paths will be inferred from the BS-X BIOS path, not BS-X rom path.
.TP
\fB--bsxslot PATH, -B PATH\fR
Path to BS-X slotted rom. Load BS-X BIOS as the regular rom.
When using BS-X, save ram paths will be inferred from the BS-X BIOS path, not BS-X rom path.
.TP
\fB--sufamiA PATH\fR
Path to A slot in Sufami Turbo. Load Sufami Turbo BIOS as regular rom.
When using Sufami, save ram paths will be inferred from the Sufami BIOS path, not slot paths.
.TP
\fB--sufamiB PATH\fR
Path to B slot in Sufami Turbo. Load Sufami Turbo BIOS as regular rom.
When using Sufami, save ram paths will be inferred from the Sufami BIOS path, not slot paths.
\fB--subsystem SUBSYSTEM\fR
Use a subsystem of the loaded libretro core. Multiple ROMs are loaded as multiple arguments.
If a ROM is skipped, use a blank ("") command line argument.
ROMs must be loaded in an order which depends on the particular subsystem used.
See verbose log output to learn how a particular subsystem wants ROMs to be loaded.
.TP
\fB--mouse PORT, -m PORT\fR

View File

@ -185,6 +185,21 @@ void libretro_free_system_info(struct retro_system_info *info)
memset(info, 0, sizeof(*info));
}
const struct retro_subsystem_info *libretro_find_subsystem_info(const struct retro_subsystem_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,34 @@ 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_SUBSYSTEM_INFO:
{
RARCH_LOG("Environ SET_SUBSYSTEM_INFO.\n");
unsigned i, j;
const struct retro_subsystem_info *info = (const struct retro_subsystem_info*)data;
for (i = 0; info[i].ident; i++)
{
RARCH_LOG("Special game type: %s\n", info[i].desc);
RARCH_LOG(" Ident: %s\n", info[i].ident);
RARCH_LOG(" ID: %u\n", info[i].id);
RARCH_LOG(" ROMs:\n");
for (j = 0; j < info[i].num_roms; j++)
{
RARCH_LOG(" %s (%s)\n",
info[i].roms[j].desc, info[i].roms[j].required ? "required" : "optional");
}
}
free(g_extern.system.special);
g_extern.system.special = (struct retro_subsystem_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));
g_extern.system.num_special = i;
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_subsystem_info *libretro_find_subsystem_info(const struct retro_subsystem_info *info, unsigned num_info, const char *ident);
extern void (*pretro_init)(void);
extern void (*pretro_deinit)(void);

350
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);
@ -231,6 +203,13 @@ bool save_state(const char *path)
return ret;
}
struct sram_block
{
unsigned type;
void *data;
size_t size;
};
bool load_state(const char *path)
{
unsigned i;
@ -248,75 +227,55 @@ bool load_state(const char *path)
bool ret = true;
RARCH_LOG("State size: %u bytes.\n", (unsigned)size);
void *block_buf[2] = {NULL, NULL};
int block_type[2] = {-1, -1};
size_t block_size[2] = {0};
struct sram_block *blocks = NULL;
unsigned num_blocks = 0;
if (g_settings.block_sram_overwrite)
if (g_settings.block_sram_overwrite && g_extern.savefiles && g_extern.savefiles->size)
{
RARCH_LOG("Blocking SRAM overwrite.\n");
switch (g_extern.game_type)
blocks = (struct sram_block*)calloc(g_extern.savefiles->size, sizeof(*blocks));
if (blocks)
{
case RARCH_CART_NORMAL:
block_type[0] = RETRO_MEMORY_SAVE_RAM;
block_type[1] = RETRO_MEMORY_RTC;
break;
case RARCH_CART_BSX:
case RARCH_CART_BSX_SLOTTED:
block_type[0] = RETRO_MEMORY_SNES_BSX_RAM;
block_type[1] = RETRO_MEMORY_SNES_BSX_PRAM;
break;
case RARCH_CART_SUFAMI:
block_type[0] = RETRO_MEMORY_SNES_SUFAMI_TURBO_A_RAM;
block_type[1] = RETRO_MEMORY_SNES_SUFAMI_TURBO_B_RAM;
break;
case RARCH_CART_SGB:
block_type[0] = RETRO_MEMORY_SNES_GAME_BOY_RAM;
block_type[1] = RETRO_MEMORY_SNES_GAME_BOY_RTC;
break;
num_blocks = g_extern.savefiles->size;
for (i = 0; i < num_blocks; i++)
blocks[i].type = g_extern.savefiles->elems[i].attr.i;
}
}
for (i = 0; i < 2; i++)
if (block_type[i] != -1)
block_size[i] = pretro_get_memory_size(block_type[i]);
for (i = 0; i < num_blocks; i++)
blocks[i].size = pretro_get_memory_size(blocks[i].type);
for (i = 0; i < 2; i++)
if (block_size[i])
block_buf[i] = malloc(block_size[i]);
for (i = 0; i < num_blocks; i++)
if (blocks[i].size)
blocks[i].data = malloc(blocks[i].size);
// Backup current SRAM which is overwritten by unserialize.
for (i = 0; i < 2; i++)
for (i = 0; i < num_blocks; i++)
{
if (block_buf[i])
if (blocks[i].data)
{
const void *ptr = pretro_get_memory_data(block_type[i]);
const void *ptr = pretro_get_memory_data(blocks[i].type);
if (ptr)
memcpy(block_buf[i], ptr, block_size[i]);
memcpy(blocks[i].data, ptr, blocks[i].size);
}
}
ret = pretro_unserialize(buf, size);
// Flush back :D
for (i = 0; i < 2 && ret; i++)
for (i = 0; i < num_blocks; i++)
{
if (block_buf[i])
if (blocks[i].data)
{
void *ptr = pretro_get_memory_data(block_type[i]);
void *ptr = pretro_get_memory_data(blocks[i].type);
if (ptr)
memcpy(ptr, block_buf[i], block_size[i]);
memcpy(ptr, blocks[i].data, blocks[i].size);
}
}
for (i = 0; i < 2; i++)
if (block_buf[i])
free(block_buf[i]);
free(buf);
for (i = 0; i < num_blocks; i++)
free(blocks[i].data);
free(blocks);
return ret;
}
@ -362,175 +321,164 @@ 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_subsystem_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(*roms->elems[0].data ? info : NULL);
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_subsystem_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.libretro_no_rom ? "" : 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,
*g_settings.extraction_directory ? g_settings.extraction_directory : NULL))
{
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_set(roms, i, temporary_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

@ -308,6 +308,7 @@ end:
struct zip_extract_userdata
{
char *zip_path;
const char *extraction_directory;
size_t zip_path_size;
struct string_list *ext;
bool found_rom;
@ -323,8 +324,13 @@ static bool zip_extract_cb(const char *name, const uint8_t *cdata, unsigned cmod
if (ext && string_list_find_elem(data->ext, ext))
{
char new_path[PATH_MAX];
fill_pathname_resolve_relative(new_path, data->zip_path,
path_basename(name), sizeof(new_path));
if (data->extraction_directory)
fill_pathname_join(new_path, data->extraction_directory,
path_basename(name), sizeof(new_path));
else
fill_pathname_resolve_relative(new_path, data->zip_path,
path_basename(name), sizeof(new_path));
switch (cmode)
{
@ -350,7 +356,8 @@ static bool zip_extract_cb(const char *name, const uint8_t *cdata, unsigned cmod
return true;
}
bool zlib_extract_first_rom(char *zip_path, size_t zip_path_size, const char *valid_exts)
bool zlib_extract_first_rom(char *zip_path, size_t zip_path_size, const char *valid_exts,
const char *extraction_directory)
{
bool ret;
struct zip_extract_userdata userdata = {0};
@ -369,6 +376,7 @@ bool zlib_extract_first_rom(char *zip_path, size_t zip_path_size, const char *va
userdata.zip_path = zip_path;
userdata.zip_path_size = zip_path_size;
userdata.extraction_directory = extraction_directory;
userdata.ext = list;
if (!zlib_parse_file(zip_path, zip_extract_cb, &userdata))

View File

@ -30,7 +30,7 @@ typedef bool (*zlib_file_cb)(const char *name,
bool zlib_parse_file(const char *file, zlib_file_cb file_cb, void *userdata);
// Built with zlib_parse_file.
bool zlib_extract_first_rom(char *zip_path, size_t zip_path_size, const char *valid_exts);
bool zlib_extract_first_rom(char *zip_path, size_t zip_path_size, const char *valid_exts, const char *extraction_dir);
struct string_list *zlib_get_file_list(const char *path);
bool zlib_inflate_data_to_file(const char *path, const uint8_t *data,

View File

@ -211,6 +211,12 @@ bool string_list_append(struct string_list *list, const char *elem, union string
return true;
}
void string_list_set(struct string_list *list, unsigned index, const char *str)
{
free(list->elems[index].data);
rarch_assert(list->elems[index].data = strdup(str));
}
void string_list_join_concat(char *buffer, size_t size, const struct string_list *list, const char *sep)
{
size_t len = strlen(buffer);

View File

@ -61,6 +61,7 @@ struct string_list *string_list_new(void);
bool string_list_append(struct string_list *list, const char *elem, union string_list_elem_attr attr);
void string_list_free(struct string_list *list);
void string_list_join_concat(char *buffer, size_t size, const struct string_list *list, const char *sep);
void string_list_set(struct string_list *list, unsigned index, const char *str);
bool path_is_directory(const char *path);
bool path_file_exists(const char *path);

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

@ -286,6 +286,8 @@ struct settings
char screenshot_directory[PATH_MAX];
char system_directory[PATH_MAX];
char extraction_directory[PATH_MAX];
bool rewind_enable;
size_t rewind_buffer_size;
unsigned rewind_granularity;
@ -316,15 +318,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 +351,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 +378,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 +444,9 @@ struct global
retro_usec_t frame_time_last;
core_option_manager_t *core_options;
struct retro_subsystem_info *special;
unsigned num_special;
} system;
struct
@ -563,7 +562,8 @@ struct global
unsigned turbo_count;
// Autosave support.
autosave_t *autosave[2];
autosave_t **autosave;
unsigned num_autosave;
// Netplay.
#ifdef HAVE_NETPLAY

View File

@ -115,6 +115,21 @@ void retro_set_environment(retro_environment_t cb)
if (!cb(RETRO_ENVIRONMENT_GET_LOG_INTERFACE, &logging))
logging.log = fallback_log;
static const struct retro_subsystem_memory_info mem1[] = {{ "ram1", 0x400 }, { "ram2", 0x401 }};
static const struct retro_subsystem_memory_info mem2[] = {{ "ram3", 0x402 }, { "ram4", 0x403 }};
static const struct retro_subsystem_rom_info roms[] = {
{ "Test Rom #1", "bin", false, false, true, mem1, 2, },
{ "Test Rom #2", "bin", false, false, true, mem2, 2, },
};
static const struct retro_subsystem_info types[] = {
{ "Foo", "foo", roms, 2, 0x200, },
{ NULL },
};
cb(RETRO_ENVIRONMENT_SET_SUBSYSTEM_INFO, (void*)types);
}
void retro_set_audio_sample(retro_audio_sample_t cb)
@ -370,10 +385,11 @@ unsigned retro_get_region(void)
bool retro_load_game_special(unsigned type, const struct retro_game_info *info, size_t num)
{
(void)type;
(void)info;
(void)num;
return false;
if (type != 0x200)
return false;
if (num != 2)
return false;
return retro_load_game(NULL);
}
size_t retro_serialize_size(void)

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,58 @@ 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_SUBSYSTEM_INFO 34
// const struct retro_subsystem_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_subsystem_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_subsystem_rom_info
{
const char *desc; // Describes what the ROM is (SGB bios, GB rom, etc).
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_subsystem_memory_info *memory;
unsigned num_memory;
};
struct retro_subsystem_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_subsystem_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

@ -776,11 +776,10 @@ static void print_help(void)
#ifdef HAVE_DYNAMIC
puts("\t-L/--libretro: Path to libretro implementation. Overrides any config setting.");
#endif
puts("\t-g/--gameboy: Path to Gameboy ROM. Load SuperGameBoy as the regular rom.");
puts("\t-b/--bsx: Path to BSX rom. Load BSX BIOS as the regular rom.");
puts("\t-B/--bsxslot: Path to BSX slotted rom. Load BSX BIOS as the regular rom.");
puts("\t--sufamiA: Path to A slot of Sufami Turbo. Load Sufami base cart as regular rom.");
puts("\t--sufamiB: Path to B slot of Sufami Turbo.");
puts("\t--subsystem: Use a subsystem of the libretro core. Multiple ROMs are loaded as multiple arguments.");
puts("\t\tIf a ROM is skipped, use a blank (\"\") command line argument");
puts("\t\tROMs must be loaded in an order which depends on the particular subsystem used.");
puts("\t\tSee verbose log output to learn how a particular subsystem wants ROMs to be loaded.");
printf("\t-N/--nodevice: Disconnects controller device connected to port (1 to %d).\n", MAX_PLAYERS);
printf("\t-A/--dualanalog: Connect a DualAnalog controller to port (1 to %d).\n", MAX_PLAYERS);
@ -836,19 +835,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))
{
@ -869,6 +902,7 @@ static void parse_input(int argc, char *argv[])
g_extern.has_set_save_path = false;
g_extern.has_set_state_path = false;
g_extern.has_set_libretro = false;
*g_extern.subsystem = '\0';
if (argc < 2)
{
@ -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,11 @@ 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.autosave = NULL;
g_extern.num_autosave = 0;
}
#endif
@ -1838,84 +1775,86 @@ 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_subsystem_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 = min(info ? info->num_roms : 0, g_extern.subsystem_fullpaths ? g_extern.subsystem_fullpaths->size : 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_subsystem_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 +2469,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 +2816,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 +2948,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 +3218,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

@ -8,6 +8,10 @@
# This will be overridden by explicit command line options.
# savestate_directory =
# If set to a directory, ROMs which are temporarily extracted
# will be extracted to this directory.
# extraction_directory =
# Automatically saves a savestate at the end of RetroArch's lifetime.
# The path is $SRAM_PATH.auto.
# RetroArch will automatically load any savestate with this path on startup if savestate_auto_load is set.

View File

@ -376,6 +376,7 @@ void config_set_defaults(void)
*g_settings.cheat_settings_path = '\0';
*g_settings.screenshot_directory = '\0';
*g_settings.system_directory = '\0';
*g_settings.extraction_directory = '\0';
*g_settings.input.autoconfig_dir = '\0';
*g_settings.input.overlay = '\0';
*g_settings.content_directory = '\0';
@ -929,6 +930,7 @@ bool config_load_file(const char *path, bool set_defaults)
}
}
CONFIG_GET_PATH(extraction_directory, "extraction_directory");
CONFIG_GET_PATH(content_directory, "content_directory");
if (!strcmp(g_settings.content_directory, "default"))
*g_settings.content_directory = '\0';
@ -1009,8 +1011,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");
@ -1276,6 +1278,7 @@ bool config_save_file(const char *path)
config_set_int(conf, "audio_out_rate", g_settings.audio.out_rate);
config_set_path(conf, "system_directory", *g_settings.system_directory ? g_settings.system_directory : "default");
config_set_path(conf, "extraction_directory", g_settings.extraction_directory);
config_set_string(conf, "audio_resampler", g_settings.audio.resampler);
config_set_path(conf, "savefile_directory", *g_extern.savefile_dir ? g_extern.savefile_dir : "default");
config_set_path(conf, "savestate_directory", *g_extern.savestate_dir ? g_extern.savestate_dir : "default");