mirror of
https://github.com/libretro/RetroArch
synced 2025-03-26 02:37:23 +00:00
Merge pull request #326 from libretro/coreinfo
Integration of core info files in RGUI.
This commit is contained in:
commit
8a674d07e9
1
Makefile
1
Makefile
@ -22,6 +22,7 @@ OBJ = frontend/frontend.o \
|
||||
core_options.o \
|
||||
compat/compat.o \
|
||||
cheats.o \
|
||||
core_info.o \
|
||||
conf/config_file.o \
|
||||
screenshot.o \
|
||||
gfx/scaler/scaler.o \
|
||||
|
@ -23,6 +23,7 @@ OBJ = frontend/frontend.o \
|
||||
compat/compat.o \
|
||||
screenshot.o \
|
||||
cheats.o \
|
||||
core_info.o \
|
||||
audio/utils.o \
|
||||
input/overlay.o \
|
||||
fifo_buffer.o \
|
||||
|
@ -26,7 +26,7 @@ NSArray* apple_get_modules()
|
||||
{
|
||||
if (!moduleList)
|
||||
{
|
||||
coreList = get_core_info_list(apple_platform.coreDirectory.UTF8String);
|
||||
coreList = core_info_list_new(apple_platform.coreDirectory.UTF8String);
|
||||
|
||||
if (!coreList)
|
||||
return nil;
|
||||
@ -182,4 +182,4 @@ static NSString* build_string_pair(NSString* stringA, NSString* stringB)
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
@ -87,7 +87,7 @@ RetroArch::RetroArch()
|
||||
//Get core DropDown reference to populate it in C++
|
||||
coreSelection = mAppPane->findChild<DropDown*>("dropdown_core");
|
||||
connect(coreSelection, SIGNAL(selectedValueChanged(QVariant)), this, SLOT(onCoreSelected(QVariant)));
|
||||
core_info_list = get_core_info_list(g_settings.libretro);
|
||||
core_info_list = core_info_list_new(g_settings.libretro);
|
||||
populateCores(core_info_list);
|
||||
|
||||
Application::instance()->setScene(mAppPane);
|
||||
@ -112,7 +112,7 @@ RetroArch::RetroArch()
|
||||
|
||||
RetroArch::~RetroArch()
|
||||
{
|
||||
free_core_info_list(core_info_list);
|
||||
core_info_list_free(core_info_list);
|
||||
}
|
||||
|
||||
void RetroArch::aboutToQuit()
|
||||
|
178
core_info.c
178
core_info.c
@ -17,55 +17,58 @@
|
||||
#include "general.h"
|
||||
#include "file.h"
|
||||
#include "file_ext.h"
|
||||
#include "file_extract.h"
|
||||
#include "config.def.h"
|
||||
|
||||
core_info_list_t *get_core_info_list(const char *modules_path)
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
core_info_list_t *core_info_list_new(const char *modules_path)
|
||||
{
|
||||
struct string_list *contents = dir_list_new(modules_path, EXT_EXECUTABLES, false);
|
||||
|
||||
core_info_t *core_info;
|
||||
core_info_list_t *core_info_list;
|
||||
unsigned i;
|
||||
core_info_t *core_info = NULL;
|
||||
core_info_list_t *core_info_list = NULL;
|
||||
|
||||
if (!contents)
|
||||
return NULL;
|
||||
|
||||
core_info = (core_info_t*)malloc(contents->size * sizeof(core_info_t));
|
||||
memset(core_info, 0, contents->size * sizeof(core_info_t));
|
||||
core_info_list = (core_info_list_t*)calloc(1, sizeof(*core_info_list));
|
||||
if (!core_info_list)
|
||||
goto error;
|
||||
|
||||
core_info = (core_info_t*)calloc(contents->size, sizeof(*core_info));
|
||||
if (!core_info)
|
||||
goto error;
|
||||
|
||||
core_info_list = (core_info_list_t*)malloc(sizeof(core_info_list_t));
|
||||
memset(core_info_list, 0, sizeof(core_info_list_t));
|
||||
core_info_list->list = core_info;
|
||||
core_info_list->count = contents->size;
|
||||
|
||||
for (i = 0; i < contents->size; i ++)
|
||||
for (size_t i = 0; i < contents->size; i++)
|
||||
{
|
||||
char buffer[PATH_MAX];
|
||||
char info_path[PATH_MAX];
|
||||
char *substr;
|
||||
|
||||
core_info[i].path = strdup(contents->elems[i].data);
|
||||
if (!core_info[i].path)
|
||||
break;
|
||||
|
||||
// NOTE: This assumes all modules are named module_name_{tag}.ext
|
||||
// {tag} must not contain an underscore. (This isn't true for PC versions)
|
||||
snprintf(buffer, PATH_MAX, "%s", contents->elems[i].data);
|
||||
substr = strrchr(buffer, '_');
|
||||
#if defined(IOS) || defined(HAVE_BB10) || defined(__QNX__)
|
||||
// Libs are deployed with a suffix (*_ios.dylib, *_qnx.so, etc).
|
||||
char buffer[PATH_MAX];
|
||||
strlcpy(buffer, contents->elems[i].data, sizeof(buffer));
|
||||
char *substr = strrchr(buffer, '_');
|
||||
if (substr)
|
||||
*substr = 0;
|
||||
|
||||
// NOTE: Can't just use fill_pathname on iOS as it will cut at RetroArch.app;
|
||||
// perhaps fill_pathname shouldn't cut before the last path element.
|
||||
if (substr)
|
||||
snprintf(info_path, PATH_MAX, "%s.info", buffer);
|
||||
else
|
||||
fill_pathname(info_path, buffer, ".info", PATH_MAX);
|
||||
*substr = '\0';
|
||||
fill_pathname(info_path, buffer, ".info", sizeof(info_path));
|
||||
#else
|
||||
fill_pathname(info_path, core_info[i].path, ".info", sizeof(info_path));
|
||||
#endif
|
||||
|
||||
core_info[i].data = config_file_new(info_path);
|
||||
|
||||
if (core_info[i].data)
|
||||
{
|
||||
config_get_string(core_info[i].data, "display_name", &core_info[i].display_name);
|
||||
|
||||
if (config_get_string(core_info[i].data, "supported_extensions", &core_info[i].supported_extensions) &&
|
||||
core_info[i].supported_extensions)
|
||||
core_info[i].supported_extensions_list = string_split(core_info[i].supported_extensions, "|");
|
||||
@ -75,19 +78,48 @@ core_info_list_t *get_core_info_list(const char *modules_path)
|
||||
core_info[i].display_name = strdup(path_basename(core_info[i].path));
|
||||
}
|
||||
|
||||
dir_list_free(contents);
|
||||
size_t all_ext_len = 0;
|
||||
for (size_t i = 0; i < core_info_list->count; i++)
|
||||
{
|
||||
all_ext_len += core_info_list->list[i].supported_extensions ?
|
||||
(strlen(core_info_list->list[i].supported_extensions) + 2) : 0;
|
||||
}
|
||||
|
||||
if (all_ext_len)
|
||||
{
|
||||
all_ext_len += strlen("|zip");
|
||||
core_info_list->all_ext = (char*)calloc(1, all_ext_len);
|
||||
}
|
||||
|
||||
if (core_info_list->all_ext)
|
||||
{
|
||||
for (size_t i = 0; i < core_info_list->count; i++)
|
||||
{
|
||||
if (core_info_list->list[i].supported_extensions)
|
||||
{
|
||||
strlcat(core_info_list->all_ext, core_info_list->list[i].supported_extensions, all_ext_len);
|
||||
strlcat(core_info_list->all_ext, "|", all_ext_len);
|
||||
}
|
||||
}
|
||||
strlcat(core_info_list->all_ext, "|zip", all_ext_len);
|
||||
}
|
||||
|
||||
dir_list_free(contents);
|
||||
return core_info_list;
|
||||
|
||||
error:
|
||||
if (contents)
|
||||
dir_list_free(contents);
|
||||
core_info_list_free(core_info_list);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void free_core_info_list(core_info_list_t *core_info_list)
|
||||
void core_info_list_free(core_info_list_t *core_info_list)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!core_info_list)
|
||||
return;
|
||||
|
||||
for (i = 0; i < core_info_list->count; i++)
|
||||
for (size_t i = 0; i < core_info_list->count; i++)
|
||||
{
|
||||
free(core_info_list->list[i].path);
|
||||
free(core_info_list->list[i].display_name);
|
||||
@ -96,11 +128,38 @@ void free_core_info_list(core_info_list_t *core_info_list)
|
||||
config_file_free(core_info_list->list[i].data);
|
||||
}
|
||||
|
||||
free(core_info_list->all_ext);
|
||||
free(core_info_list->list);
|
||||
free(core_info_list);
|
||||
}
|
||||
|
||||
bool does_core_support_file(core_info_t* core, const char *path)
|
||||
bool core_info_list_get_display_name(core_info_list_t *core_info_list, const char *path, char *buf, size_t size)
|
||||
{
|
||||
for (size_t i = 0; i < core_info_list->count; i++)
|
||||
{
|
||||
const core_info_t *info = &core_info_list->list[i];
|
||||
if (!strcmp(info->path, path) && info->display_name)
|
||||
{
|
||||
strlcpy(buf, info->display_name, size);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool core_info_does_support_any_file(const core_info_t *core, const struct string_list *list)
|
||||
{
|
||||
if (!list || !core || !core->supported_extensions_list)
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < list->size; i++)
|
||||
if (string_list_find_elem_prefix(core->supported_extensions_list, ".", path_get_extension(list->elems[i].data)))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool core_info_does_support_file(const core_info_t *core, const char *path)
|
||||
{
|
||||
if (!path || !core || !core->supported_extensions_list)
|
||||
return false;
|
||||
@ -108,3 +167,60 @@ bool does_core_support_file(core_info_t* core, const char *path)
|
||||
return string_list_find_elem_prefix(core->supported_extensions_list, ".", path_get_extension(path));
|
||||
}
|
||||
|
||||
const char *core_info_list_get_all_extensions(core_info_list_t *core_info_list)
|
||||
{
|
||||
return core_info_list->all_ext;
|
||||
}
|
||||
|
||||
// qsort_r() is not in standard C, sadly.
|
||||
static const char *core_info_tmp_path;
|
||||
static const struct string_list *core_info_tmp_list;
|
||||
|
||||
static int core_info_qsort_cmp(const void *a_, const void *b_)
|
||||
{
|
||||
const core_info_t *a = (const core_info_t*)a_;
|
||||
const core_info_t *b = (const core_info_t*)b_;
|
||||
|
||||
int support_a = core_info_does_support_any_file(a, core_info_tmp_list) ||
|
||||
core_info_does_support_file(a, core_info_tmp_path);
|
||||
int support_b = core_info_does_support_any_file(b, core_info_tmp_list) ||
|
||||
core_info_does_support_file(b, core_info_tmp_path);
|
||||
|
||||
if (support_a != support_b)
|
||||
return support_b - support_a;
|
||||
else
|
||||
return strcasecmp(a->display_name, b->display_name);
|
||||
}
|
||||
|
||||
void core_info_list_get_supported_cores(core_info_list_t *core_info_list, const char *path,
|
||||
const core_info_t **infos, size_t *num_infos)
|
||||
{
|
||||
core_info_tmp_path = path;
|
||||
|
||||
#ifdef HAVE_ZLIB
|
||||
struct string_list *list = NULL;
|
||||
if (!strcasecmp(path_get_extension(path), "zip"))
|
||||
list = zlib_get_file_list(path);
|
||||
core_info_tmp_list = list;
|
||||
#endif
|
||||
|
||||
// Let supported core come first in list so we can return a pointer to them.
|
||||
qsort(core_info_list->list, core_info_list->count, sizeof(core_info_t), core_info_qsort_cmp);
|
||||
|
||||
size_t supported = 0;
|
||||
for (size_t i = 0; i < core_info_list->count; i++, supported++)
|
||||
{
|
||||
const core_info_t *core = &core_info_list->list[i];
|
||||
if (!core_info_does_support_file(core, path) && !core_info_does_support_any_file(core, list))
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef HAVE_ZLIB
|
||||
if (list)
|
||||
string_list_free(list);
|
||||
#endif
|
||||
|
||||
*infos = core_info_list->list;
|
||||
*num_infos = supported;
|
||||
}
|
||||
|
||||
|
34
core_info.h
34
core_info.h
@ -16,29 +16,41 @@
|
||||
#ifndef CORE_INFO_H_
|
||||
#define CORE_INFO_H_
|
||||
|
||||
#include "conf/config_file.h"
|
||||
#include "file.h"
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "conf/config_file.h"
|
||||
|
||||
typedef struct {
|
||||
char * path;
|
||||
config_file_t* data;
|
||||
char * display_name;
|
||||
char * supported_extensions;
|
||||
struct string_list * supported_extensions_list;
|
||||
char *path;
|
||||
config_file_t *data;
|
||||
char *display_name;
|
||||
char *supported_extensions;
|
||||
struct string_list *supported_extensions_list;
|
||||
} core_info_t;
|
||||
|
||||
typedef struct {
|
||||
core_info_t *list;
|
||||
int count;
|
||||
size_t count;
|
||||
char *all_ext;
|
||||
} core_info_list_t;
|
||||
|
||||
core_info_list_t *get_core_info_list(const char *modules_path);
|
||||
void free_core_info_list(core_info_list_t * core_info_list);
|
||||
core_info_list_t *core_info_list_new(const char *modules_path);
|
||||
void core_info_list_free(core_info_list_t *core_info_list);
|
||||
|
||||
bool does_core_support_file(core_info_t* core, const char *path);
|
||||
bool core_info_does_support_file(const core_info_t *core, const char *path);
|
||||
bool core_info_does_support_any_file(const core_info_t *core, const struct string_list *list);
|
||||
|
||||
// Non-reentrant, does not allocate. Returns pointer to internal state.
|
||||
void core_info_list_get_supported_cores(core_info_list_t *core_info_list, const char *path,
|
||||
const core_info_t **infos, size_t *num_infos);
|
||||
|
||||
const char *core_info_list_get_all_extensions(core_info_list_t *core_info_list);
|
||||
|
||||
bool core_info_list_get_display_name(core_info_list_t *core_info_list, const char *path, char *buf, size_t size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
8
file.h
8
file.h
@ -68,15 +68,20 @@ void dir_list_free(struct string_list *list);
|
||||
bool string_list_find_elem(const struct string_list *list, const char *elem);
|
||||
bool string_list_find_elem_prefix(const struct string_list *list, const char *prefix, const char *elem);
|
||||
struct string_list *string_split(const char *str, const char *delim);
|
||||
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);
|
||||
|
||||
bool path_is_directory(const char *path);
|
||||
bool path_file_exists(const char *path);
|
||||
|
||||
// Gets extension of file. Only '.'s after the last slash are considered.
|
||||
const char *path_get_extension(const char *path);
|
||||
|
||||
bool path_mkdir(const char *dir);
|
||||
|
||||
// Removes all text after and including the last '.'
|
||||
// Removes all text after and including the last '.'.
|
||||
// Only '.'s after the last slash are considered.
|
||||
char *path_remove_extension(char *path);
|
||||
|
||||
// Returns basename from path.
|
||||
@ -100,6 +105,7 @@ bool path_is_absolute(const char *path);
|
||||
|
||||
// Replaces filename extension with 'replace' and outputs result to out_path.
|
||||
// The extension here is considered to be the string from the last '.' to the end.
|
||||
// Only '.'s after the last slash are considered as extensions.
|
||||
// If no '.' is present, in_path and replace will simply be concatenated.
|
||||
// 'size' is buffer size of 'out_path'.
|
||||
// E.g.: in_path = "/foo/bar/baz/boo.c", replace = ".asm" => out_path = "/foo/bar/baz/boo.asm"
|
||||
|
165
file_extract.c
165
file_extract.c
@ -18,6 +18,7 @@
|
||||
#include "compat/strl.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef WANT_MINIZ
|
||||
#include "deps/miniz/zlib.h"
|
||||
@ -46,7 +47,7 @@ static uint32_t read_le(const uint8_t *data, unsigned size)
|
||||
return val;
|
||||
}
|
||||
|
||||
static bool inflate_data_to_file(const char *path, uint8_t *cdata,
|
||||
static bool inflate_data_to_file(const char *path, const uint8_t *cdata,
|
||||
uint32_t csize, uint32_t size, uint32_t crc32)
|
||||
{
|
||||
bool ret = true;
|
||||
@ -60,7 +61,7 @@ static bool inflate_data_to_file(const char *path, uint8_t *cdata,
|
||||
if (inflateInit2(&stream, -MAX_WBITS) != Z_OK)
|
||||
GOTO_END_ERROR();
|
||||
|
||||
stream.next_in = cdata;
|
||||
stream.next_in = (uint8_t*)cdata;
|
||||
stream.avail_in = csize;
|
||||
stream.next_out = out_data;
|
||||
stream.avail_out = size;
|
||||
@ -85,24 +86,14 @@ end:
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool zlib_extract_first_rom(char *zip_path, size_t zip_path_size, const char *valid_exts)
|
||||
bool zlib_parse_file(const char *file, zlib_file_cb file_cb, void *userdata)
|
||||
{
|
||||
const uint8_t *footer = NULL;
|
||||
const uint8_t *directory = NULL;
|
||||
|
||||
bool ret = true;
|
||||
if (!valid_exts)
|
||||
{
|
||||
RARCH_ERR("Libretro implementation does not have any valid extensions. Cannot unzip without knowing this.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
struct string_list *list = string_split(valid_exts, "|");
|
||||
if (!list)
|
||||
return false;
|
||||
|
||||
uint8_t *data = NULL;
|
||||
ssize_t zip_size = read_file(zip_path, (void**)&data);
|
||||
ssize_t zip_size = read_file(file, (void**)&data);
|
||||
if (zip_size < 22)
|
||||
GOTO_END_ERROR();
|
||||
|
||||
@ -150,44 +141,126 @@ bool zlib_extract_first_rom(char *zip_path, size_t zip_path_size, const char *va
|
||||
|
||||
RARCH_LOG("OFFSET: %u, CSIZE: %u, SIZE: %u.\n", offset + 30 + offsetNL + offsetEL, csize, size);
|
||||
|
||||
// Extract first ROM that matches our list.
|
||||
const char *ext = path_get_extension(filename);
|
||||
if (ext && string_list_find_elem(list, ext))
|
||||
{
|
||||
char new_path[PATH_MAX];
|
||||
fill_pathname_resolve_relative(new_path, zip_path,
|
||||
path_basename(filename), sizeof(new_path));
|
||||
|
||||
switch (cmode)
|
||||
{
|
||||
case 0: // Uncompressed
|
||||
if (!write_file(new_path, cdata, size))
|
||||
GOTO_END_ERROR();
|
||||
goto end;
|
||||
|
||||
case 8: // Deflate
|
||||
if (inflate_data_to_file(new_path, cdata, csize, size, crc32))
|
||||
{
|
||||
strlcpy(zip_path, new_path, zip_path_size);
|
||||
goto end;
|
||||
}
|
||||
else
|
||||
GOTO_END_ERROR();
|
||||
|
||||
default:
|
||||
GOTO_END_ERROR();
|
||||
}
|
||||
}
|
||||
if (!file_cb(filename, cdata, cmode, csize, size, crc32, userdata))
|
||||
break;
|
||||
|
||||
directory += 46 + namelength + extralength + commentlength;
|
||||
}
|
||||
|
||||
RARCH_ERR("Didn't find any ROMS that matched valid extensions for libretro implementation.\n");
|
||||
GOTO_END_ERROR();
|
||||
|
||||
end:
|
||||
free(data);
|
||||
string_list_free(list);
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct zip_extract_userdata
|
||||
{
|
||||
char *zip_path;
|
||||
size_t zip_path_size;
|
||||
struct string_list *ext;
|
||||
bool found_rom;
|
||||
};
|
||||
|
||||
static bool zip_extract_cb(const char *name, const uint8_t *cdata, unsigned cmode, uint32_t csize, uint32_t size,
|
||||
uint32_t crc32, void *userdata)
|
||||
{
|
||||
struct zip_extract_userdata *data = (struct zip_extract_userdata*)userdata;
|
||||
|
||||
// Extract first ROM that matches our list.
|
||||
const char *ext = path_get_extension(name);
|
||||
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));
|
||||
|
||||
switch (cmode)
|
||||
{
|
||||
case 0: // Uncompressed
|
||||
data->found_rom = write_file(new_path, cdata, size);
|
||||
return false;
|
||||
|
||||
case 8: // Deflate
|
||||
if (inflate_data_to_file(new_path, cdata, csize, size, crc32))
|
||||
{
|
||||
strlcpy(data->zip_path, new_path, data->zip_path_size);
|
||||
data->found_rom = true;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool zlib_extract_first_rom(char *zip_path, size_t zip_path_size, const char *valid_exts)
|
||||
{
|
||||
if (!valid_exts)
|
||||
{
|
||||
RARCH_ERR("Libretro implementation does not have any valid extensions. Cannot unzip without knowing this.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ret = true;
|
||||
struct string_list *list = string_split(valid_exts, "|");
|
||||
if (!list)
|
||||
GOTO_END_ERROR();
|
||||
|
||||
struct zip_extract_userdata userdata = {0};
|
||||
userdata.zip_path = zip_path;
|
||||
userdata.zip_path_size = zip_path_size;
|
||||
userdata.ext = list;
|
||||
|
||||
if (!zlib_parse_file(zip_path, zip_extract_cb, &userdata))
|
||||
{
|
||||
RARCH_ERR("Parsing ZIP failed.\n");
|
||||
GOTO_END_ERROR();
|
||||
}
|
||||
|
||||
if (!userdata.found_rom)
|
||||
{
|
||||
RARCH_ERR("Didn't find any ROMS that matched valid extensions for libretro implementation.\n");
|
||||
GOTO_END_ERROR();
|
||||
}
|
||||
|
||||
end:
|
||||
if (list)
|
||||
string_list_free(list);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool zlib_get_file_list_cb(const char *path, const uint8_t *cdata, unsigned cmode,
|
||||
uint32_t csize, uint32_t size,
|
||||
uint32_t crc32, void *userdata)
|
||||
{
|
||||
(void)cdata;
|
||||
(void)cmode;
|
||||
(void)csize;
|
||||
(void)size;
|
||||
(void)crc32;
|
||||
struct string_list *list = (struct string_list*)userdata;
|
||||
union string_list_elem_attr attr;
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
return string_list_append(list, path, attr);
|
||||
}
|
||||
|
||||
struct string_list *zlib_get_file_list(const char *path)
|
||||
{
|
||||
struct string_list *list = string_list_new();
|
||||
if (!list)
|
||||
return NULL;
|
||||
|
||||
if (!zlib_parse_file(path, zlib_get_file_list_cb, list))
|
||||
{
|
||||
RARCH_ERR("Parsing ZIP failed.\n");
|
||||
string_list_free(list);
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
return list;
|
||||
}
|
||||
|
||||
|
@ -17,9 +17,21 @@
|
||||
#define FILE_EXTRACT_H__
|
||||
|
||||
#include "boolean.h"
|
||||
#include "file.h"
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// Returns true when parsing should continue. False to stop.
|
||||
typedef bool (*zlib_file_cb)(const char *name,
|
||||
const uint8_t *cdata, unsigned cmode, uint32_t csize, uint32_t size,
|
||||
uint32_t crc32, void *userdata);
|
||||
|
||||
// Low-level file parsing. Enumerates over all files and calls file_cb with userdata.
|
||||
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);
|
||||
struct string_list *zlib_get_file_list(const char *path);
|
||||
|
||||
#endif
|
||||
|
||||
|
10
file_path.c
10
file_path.c
@ -79,7 +79,7 @@ static bool string_list_capacity(struct string_list *list, size_t cap)
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct string_list *string_list_new(void)
|
||||
struct string_list *string_list_new(void)
|
||||
{
|
||||
struct string_list *list = (struct string_list*)calloc(1, sizeof(*list));
|
||||
if (!list)
|
||||
@ -94,7 +94,7 @@ static struct string_list *string_list_new(void)
|
||||
return list;
|
||||
}
|
||||
|
||||
static bool string_list_append(struct string_list *list, const char *elem, union string_list_elem_attr attr)
|
||||
bool string_list_append(struct string_list *list, const char *elem, union string_list_elem_attr attr)
|
||||
{
|
||||
if (list->size >= list->cap &&
|
||||
!string_list_capacity(list, list->cap * 2))
|
||||
@ -180,7 +180,7 @@ bool string_list_find_elem_prefix(const struct string_list *list, const char *pr
|
||||
|
||||
const char *path_get_extension(const char *path)
|
||||
{
|
||||
const char *ext = strrchr(path, '.');
|
||||
const char *ext = strrchr(path_basename(path), '.');
|
||||
if (ext)
|
||||
return ext + 1;
|
||||
else
|
||||
@ -189,7 +189,7 @@ const char *path_get_extension(const char *path)
|
||||
|
||||
char *path_remove_extension(char *path)
|
||||
{
|
||||
char *last = strrchr(path, '.');
|
||||
char *last = strrchr(path_basename(path), '.');
|
||||
if (*last)
|
||||
*last = '\0';
|
||||
return last;
|
||||
@ -417,7 +417,7 @@ void fill_pathname(char *out_path, const char *in_path, const char *replace, siz
|
||||
char tmp_path[PATH_MAX];
|
||||
|
||||
rarch_assert(strlcpy(tmp_path, in_path, sizeof(tmp_path)) < sizeof(tmp_path));
|
||||
char *tok = strrchr(tmp_path, '.');
|
||||
char *tok = strrchr(path_basename(tmp_path), '.');
|
||||
if (tok)
|
||||
*tok = '\0';
|
||||
|
||||
|
@ -408,6 +408,11 @@ static void menu_update_libretro_info(void)
|
||||
#else
|
||||
retro_get_system_info(&rgui->info);
|
||||
#endif
|
||||
|
||||
core_info_list_free(rgui->core_info);
|
||||
rgui->core_info = NULL;
|
||||
if (*rgui->libretro_dir)
|
||||
rgui->core_info = core_info_list_new(rgui->libretro_dir);
|
||||
}
|
||||
|
||||
bool load_menu_game(void)
|
||||
@ -513,6 +518,7 @@ void menu_free(void)
|
||||
#endif
|
||||
|
||||
rom_history_free(rgui->history);
|
||||
core_info_list_free(rgui->core_info);
|
||||
|
||||
free(rgui);
|
||||
}
|
||||
@ -931,3 +937,37 @@ void menu_key_event(bool down, unsigned keycode, uint32_t character, uint16_t ke
|
||||
(void)key_modifiers;
|
||||
}
|
||||
|
||||
void menu_resolve_libretro_names(rgui_list_t *list, const char *dir)
|
||||
{
|
||||
for (size_t i = 0; i < list->size; i++)
|
||||
{
|
||||
const char *path;
|
||||
unsigned type = 0;
|
||||
rgui_list_get_at_offset(list, i, &path, &type);
|
||||
if (type != RGUI_FILE_PLAIN)
|
||||
continue;
|
||||
|
||||
char core_path[PATH_MAX];
|
||||
fill_pathname_join(core_path, dir, path, sizeof(core_path));
|
||||
|
||||
char display_name[256];
|
||||
if (rgui->core_info &&
|
||||
core_info_list_get_display_name(rgui->core_info,
|
||||
core_path, display_name, sizeof(display_name)))
|
||||
rgui_list_set_alt_at_offset(list, i, display_name);
|
||||
}
|
||||
}
|
||||
|
||||
void menu_resolve_supported_cores(rgui_handle_t *rgui)
|
||||
{
|
||||
const core_info_t *info = NULL;
|
||||
size_t cores = 0;
|
||||
core_info_list_get_supported_cores(rgui->core_info, rgui->deferred_path, &info, &cores);
|
||||
for (size_t i = 0; i < cores; i++)
|
||||
{
|
||||
rgui_list_push(rgui->selection_buf, info[i].path, RGUI_FILE_PLAIN, 0);
|
||||
rgui_list_set_alt_at_offset(rgui->selection_buf, i, info[i].display_name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -24,6 +24,7 @@
|
||||
#endif
|
||||
|
||||
#include "../../performance.h"
|
||||
#include "../../core_info.h"
|
||||
|
||||
#ifdef HAVE_RGUI
|
||||
#define MENU_TEXTURE_FULLSCREEN false
|
||||
@ -99,8 +100,10 @@ typedef enum
|
||||
|
||||
// settings options are done here too
|
||||
RGUI_SETTINGS_OPEN_FILEBROWSER,
|
||||
RGUI_SETTINGS_OPEN_FILEBROWSER_DEFERRED_CORE,
|
||||
RGUI_SETTINGS_OPEN_HISTORY,
|
||||
RGUI_SETTINGS_CORE,
|
||||
RGUI_SETTINGS_DEFERRED_CORE,
|
||||
RGUI_SETTINGS_CONFIG,
|
||||
RGUI_SETTINGS_SAVE_CONFIG,
|
||||
RGUI_SETTINGS_CORE_OPTIONS,
|
||||
@ -290,6 +293,10 @@ typedef struct
|
||||
bool msg_force;
|
||||
bool push_start_screen;
|
||||
|
||||
core_info_list_t *core_info;
|
||||
bool defer_core;
|
||||
char deferred_path[PATH_MAX];
|
||||
|
||||
// Quick jumping indices with L/R.
|
||||
// Rebuilt when parsing directory.
|
||||
size_t scroll_indices[2 * (26 + 2) + 1];
|
||||
@ -346,6 +353,9 @@ void shader_manager_set_preset(struct gfx_shader *shader,
|
||||
|
||||
void menu_ticker_line(char *buf, size_t len, unsigned tick, const char *str, bool selected);
|
||||
|
||||
void menu_resolve_libretro_names(rgui_list_t *list, const char *dir);
|
||||
void menu_resolve_supported_cores(rgui_handle_t *rgui);
|
||||
|
||||
void load_menu_game_prepare(void);
|
||||
bool load_menu_game(void);
|
||||
void load_menu_game_history(unsigned game_index);
|
||||
|
@ -565,7 +565,11 @@ int menu_set_settings(unsigned setting, unsigned action)
|
||||
#ifdef HAVE_DYNAMIC
|
||||
case RGUI_LIBRETRO_DIR_PATH:
|
||||
if (action == RGUI_ACTION_START)
|
||||
{
|
||||
*rgui->libretro_dir = '\0';
|
||||
core_info_list_free(rgui->core_info);
|
||||
rgui->core_info = NULL;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case RGUI_CONFIG_DIR_PATH:
|
||||
|
@ -203,32 +203,6 @@ static int rgui_core_setting_toggle(unsigned setting, rgui_action_t action)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rgui_resolve_libretro_names(rgui_list_t *list, const char *dir)
|
||||
{
|
||||
for (size_t i = 0; i < list->size; i++)
|
||||
{
|
||||
const char *path;
|
||||
unsigned type = 0;
|
||||
rgui_list_get_at_offset(list, i, &path, &type);
|
||||
if (type != RGUI_FILE_PLAIN)
|
||||
continue;
|
||||
|
||||
char core_path[PATH_MAX];
|
||||
fill_pathname_join(core_path, dir, path, sizeof(core_path));
|
||||
char info_path[PATH_MAX];
|
||||
fill_pathname(info_path, core_path, ".info", sizeof(info_path));
|
||||
|
||||
config_file_t *conf = config_file_new(info_path);
|
||||
if (!conf)
|
||||
continue;
|
||||
|
||||
char display_name[256];
|
||||
if (config_get_array(conf, "display_name", display_name, sizeof(display_name)))
|
||||
rgui_list_set_alt_at_offset(list, i, display_name);
|
||||
config_file_free(conf);
|
||||
}
|
||||
}
|
||||
|
||||
static int rgui_settings_toggle_setting(rgui_handle_t *rgui, unsigned setting, rgui_action_t action, unsigned menu_type)
|
||||
{
|
||||
#ifdef HAVE_SHADER_MANAGER
|
||||
@ -279,7 +253,18 @@ static void rgui_settings_populate_entries(rgui_handle_t *rgui)
|
||||
#endif
|
||||
if (rgui->history)
|
||||
rgui_list_push(rgui->selection_buf, "Load Game (History)", RGUI_SETTINGS_OPEN_HISTORY, 0);
|
||||
rgui_list_push(rgui->selection_buf, "Load Game", RGUI_SETTINGS_OPEN_FILEBROWSER, 0);
|
||||
|
||||
if (rgui->core_info && rgui->core_info->count)
|
||||
rgui_list_push(rgui->selection_buf, "Load Game (Detect Core)", RGUI_SETTINGS_OPEN_FILEBROWSER_DEFERRED_CORE, 0);
|
||||
|
||||
if (rgui->info.library_name || g_extern.system.info.library_name)
|
||||
{
|
||||
char load_game_core_msg[64];
|
||||
snprintf(load_game_core_msg, sizeof(load_game_core_msg), "Load Game (%s)",
|
||||
rgui->info.library_name ? rgui->info.library_name : g_extern.system.info.library_name);
|
||||
rgui_list_push(rgui->selection_buf, load_game_core_msg, RGUI_SETTINGS_OPEN_FILEBROWSER, 0);
|
||||
}
|
||||
|
||||
rgui_list_push(rgui->selection_buf, "Core Options", RGUI_SETTINGS_CORE_OPTIONS, 0);
|
||||
rgui_list_push(rgui->selection_buf, "Video Options", RGUI_SETTINGS_VIDEO_OPTIONS, 0);
|
||||
rgui_list_push(rgui->selection_buf, "Audio Options", RGUI_SETTINGS_AUDIO_OPTIONS, 0);
|
||||
@ -866,8 +851,10 @@ static int rgui_settings_iterate(rgui_handle_t *rgui, rgui_action_t action)
|
||||
case RGUI_ACTION_RIGHT:
|
||||
case RGUI_ACTION_OK:
|
||||
case RGUI_ACTION_START:
|
||||
if (type == RGUI_SETTINGS_OPEN_FILEBROWSER && action == RGUI_ACTION_OK)
|
||||
if ((type == RGUI_SETTINGS_OPEN_FILEBROWSER || type == RGUI_SETTINGS_OPEN_FILEBROWSER_DEFERRED_CORE)
|
||||
&& action == RGUI_ACTION_OK)
|
||||
{
|
||||
rgui->defer_core = type == RGUI_SETTINGS_OPEN_FILEBROWSER_DEFERRED_CORE;
|
||||
rgui_list_push(rgui->menu_stack, rgui->base_path, RGUI_FILE_DIRECTORY, rgui->selection_ptr);
|
||||
rgui->selection_ptr = 0;
|
||||
rgui->need_refresh = true;
|
||||
@ -1140,6 +1127,8 @@ static bool rgui_directory_parse(rgui_handle_t *rgui, const char *directory, uns
|
||||
#endif
|
||||
else if (menu_type_is_directory_browser(menu_type))
|
||||
exts = ""; // we ignore files anyway
|
||||
else if (rgui->defer_core)
|
||||
exts = rgui->core_info ? core_info_list_get_all_extensions(rgui->core_info) : "";
|
||||
else if (rgui->info.valid_extensions)
|
||||
{
|
||||
exts = ext_buf;
|
||||
@ -1314,7 +1303,23 @@ static int rgui_iterate(void *data, unsigned action)
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if (menu_type == RGUI_SETTINGS_CORE)
|
||||
if (menu_type == RGUI_SETTINGS_DEFERRED_CORE)
|
||||
{
|
||||
// FIXME: Add for consoles.
|
||||
#ifdef HAVE_DYNAMIC
|
||||
strlcpy(g_settings.libretro, path, sizeof(g_settings.libretro));
|
||||
libretro_free_system_info(&rgui->info);
|
||||
libretro_get_system_info(g_settings.libretro, &rgui->info,
|
||||
&rgui->load_no_rom);
|
||||
|
||||
strlcpy(g_extern.fullpath, rgui->deferred_path, sizeof(g_extern.fullpath));
|
||||
g_extern.lifecycle_mode_state |= (1ULL << MODE_LOAD_GAME);
|
||||
rgui->msg_force = true;
|
||||
ret = -1;
|
||||
#endif
|
||||
rgui_flush_menu_stack_type(rgui, RGUI_SETTINGS);
|
||||
}
|
||||
else if (menu_type == RGUI_SETTINGS_CORE)
|
||||
{
|
||||
#if defined(HAVE_DYNAMIC)
|
||||
fill_pathname_join(g_settings.libretro, dir, path, sizeof(g_settings.libretro));
|
||||
@ -1422,6 +1427,10 @@ static int rgui_iterate(void *data, unsigned action)
|
||||
else if (menu_type == RGUI_LIBRETRO_DIR_PATH)
|
||||
{
|
||||
strlcpy(rgui->libretro_dir, dir, sizeof(rgui->libretro_dir));
|
||||
core_info_list_free(rgui->core_info);
|
||||
rgui->core_info = NULL;
|
||||
if (*rgui->libretro_dir)
|
||||
rgui->core_info = core_info_list_new(rgui->libretro_dir);
|
||||
rgui_flush_menu_stack_type(rgui, RGUI_SETTINGS_PATH_OPTIONS);
|
||||
}
|
||||
else if (menu_type == RGUI_CONFIG_DIR_PATH)
|
||||
@ -1442,12 +1451,48 @@ static int rgui_iterate(void *data, unsigned action)
|
||||
}
|
||||
else
|
||||
{
|
||||
fill_pathname_join(g_extern.fullpath, dir, path, sizeof(g_extern.fullpath));
|
||||
g_extern.lifecycle_mode_state |= (1ULL << MODE_LOAD_GAME);
|
||||
if (rgui->defer_core)
|
||||
{
|
||||
fill_pathname_join(rgui->deferred_path, dir, path, sizeof(rgui->deferred_path));
|
||||
|
||||
rgui_flush_menu_stack_type(rgui, RGUI_SETTINGS);
|
||||
rgui->msg_force = true;
|
||||
ret = -1;
|
||||
const core_info_t *info = NULL;
|
||||
size_t supported = 0;
|
||||
if (rgui->core_info)
|
||||
core_info_list_get_supported_cores(rgui->core_info, rgui->deferred_path, &info, &supported);
|
||||
|
||||
if (supported == 1) // Can make a decision right now.
|
||||
{
|
||||
strlcpy(g_extern.fullpath, rgui->deferred_path, sizeof(g_extern.fullpath));
|
||||
|
||||
strlcpy(g_settings.libretro, info->path, sizeof(g_settings.libretro));
|
||||
|
||||
#ifdef HAVE_DYNAMIC
|
||||
libretro_free_system_info(&rgui->info);
|
||||
libretro_get_system_info(g_settings.libretro, &rgui->info,
|
||||
&rgui->load_no_rom);
|
||||
#endif
|
||||
|
||||
g_extern.lifecycle_mode_state |= (1ULL << MODE_LOAD_GAME);
|
||||
rgui_flush_menu_stack_type(rgui, RGUI_SETTINGS);
|
||||
rgui->msg_force = true;
|
||||
ret = -1;
|
||||
}
|
||||
else // Present a selection.
|
||||
{
|
||||
rgui_list_push(rgui->menu_stack, rgui->libretro_dir, RGUI_SETTINGS_DEFERRED_CORE, rgui->selection_ptr);
|
||||
rgui->selection_ptr = 0;
|
||||
rgui->need_refresh = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fill_pathname_join(g_extern.fullpath, dir, path, sizeof(g_extern.fullpath));
|
||||
g_extern.lifecycle_mode_state |= (1ULL << MODE_LOAD_GAME);
|
||||
|
||||
rgui_flush_menu_stack_type(rgui, RGUI_SETTINGS);
|
||||
rgui->msg_force = true;
|
||||
ret = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -1477,6 +1522,7 @@ static int rgui_iterate(void *data, unsigned action)
|
||||
#ifdef HAVE_OVERLAY
|
||||
menu_type == RGUI_SETTINGS_OVERLAY_PRESET ||
|
||||
#endif
|
||||
menu_type == RGUI_SETTINGS_DEFERRED_CORE ||
|
||||
menu_type == RGUI_SETTINGS_CORE ||
|
||||
menu_type == RGUI_SETTINGS_CONFIG ||
|
||||
menu_type == RGUI_SETTINGS_OPEN_HISTORY ||
|
||||
@ -1488,11 +1534,13 @@ static int rgui_iterate(void *data, unsigned action)
|
||||
rgui->scroll_indices_size = 0;
|
||||
if (menu_type == RGUI_SETTINGS_OPEN_HISTORY)
|
||||
history_parse(rgui);
|
||||
else
|
||||
else if (menu_type != RGUI_SETTINGS_DEFERRED_CORE)
|
||||
rgui_directory_parse(rgui, dir, menu_type, rgui->selection_buf);
|
||||
|
||||
if (menu_type == RGUI_SETTINGS_CORE)
|
||||
rgui_resolve_libretro_names(rgui->selection_buf, dir);
|
||||
menu_resolve_libretro_names(rgui->selection_buf, dir);
|
||||
else if (menu_type == RGUI_SETTINGS_DEFERRED_CORE)
|
||||
menu_resolve_supported_cores(rgui);
|
||||
|
||||
// Before a refresh, we could have deleted a file on disk, causing
|
||||
// selection_ptr to suddendly be out of range. Ensure it doesn't overflow.
|
||||
|
@ -232,6 +232,8 @@ static void render_text(rgui_handle_t *rgui)
|
||||
|
||||
if (menu_type == RGUI_SETTINGS_CORE)
|
||||
snprintf(title, sizeof(title), "CORE SELECTION %s", dir);
|
||||
else if (menu_type == RGUI_SETTINGS_DEFERRED_CORE)
|
||||
snprintf(title, sizeof(title), "DETECTED CORES %s", dir);
|
||||
else if (menu_type == RGUI_SETTINGS_CONFIG)
|
||||
snprintf(title, sizeof(title), "CONFIG %s", dir);
|
||||
else if (menu_type == RGUI_SETTINGS_DISK_APPEND)
|
||||
@ -292,13 +294,17 @@ static void render_text(rgui_handle_t *rgui)
|
||||
snprintf(title, sizeof(title), "SYSTEM DIR %s", dir);
|
||||
else
|
||||
{
|
||||
const char *core_name = rgui->info.library_name;
|
||||
if (!core_name)
|
||||
core_name = g_extern.system.info.library_name;
|
||||
if (!core_name)
|
||||
core_name = "No Core";
|
||||
|
||||
snprintf(title, sizeof(title), "GAME (%s) %s", core_name, dir);
|
||||
if (rgui->defer_core)
|
||||
snprintf(title, sizeof(title), "GAME %s", dir);
|
||||
else
|
||||
{
|
||||
const char *core_name = rgui->info.library_name;
|
||||
if (!core_name)
|
||||
core_name = g_extern.system.info.library_name;
|
||||
if (!core_name)
|
||||
core_name = "No Core";
|
||||
snprintf(title, sizeof(title), "GAME (%s) %s", core_name, dir);
|
||||
}
|
||||
}
|
||||
|
||||
char title_buf[256];
|
||||
@ -361,9 +367,8 @@ static void render_text(rgui_handle_t *rgui)
|
||||
}
|
||||
else
|
||||
#endif
|
||||
#ifdef HAVE_DYNAMIC
|
||||
// Pretty-print libretro cores from menu.
|
||||
if (menu_type == RGUI_SETTINGS_CORE)
|
||||
if (menu_type == RGUI_SETTINGS_CORE || menu_type == RGUI_SETTINGS_DEFERRED_CORE)
|
||||
{
|
||||
if (type == RGUI_FILE_PLAIN)
|
||||
{
|
||||
@ -378,10 +383,7 @@ static void render_text(rgui_handle_t *rgui)
|
||||
w = 5;
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if (menu_type == RGUI_SETTINGS_CORE ||
|
||||
menu_type == RGUI_SETTINGS_CONFIG ||
|
||||
else if (menu_type == RGUI_SETTINGS_CONFIG ||
|
||||
#ifdef HAVE_OVERLAY
|
||||
menu_type == RGUI_SETTINGS_OVERLAY_PRESET ||
|
||||
#endif
|
||||
@ -569,6 +571,7 @@ static void render_text(rgui_handle_t *rgui)
|
||||
strlcpy(type_str, "<default>", sizeof(type_str));
|
||||
break;
|
||||
case RGUI_SETTINGS_OPEN_FILEBROWSER:
|
||||
case RGUI_SETTINGS_OPEN_FILEBROWSER_DEFERRED_CORE:
|
||||
case RGUI_SETTINGS_OPEN_HISTORY:
|
||||
case RGUI_SETTINGS_CORE_OPTIONS:
|
||||
case RGUI_SETTINGS_CUSTOM_VIEWPORT:
|
||||
|
Loading…
x
Reference in New Issue
Block a user