mirror of
https://github.com/libretro/RetroArch
synced 2025-02-01 00:32:46 +00:00
cd6e1cf3fa
and string_list_deinitialize - takes a pointer to initialize and deinitialize a string list, but importantly does not implicitly allocate and deallocate the string list pointer itself. This way, string lists can be put on the stack instead of always implicitly being put on heap - the struct itself is small enough to fit on stack
2605 lines
81 KiB
C
2605 lines
81 KiB
C
/* RetroArch - A frontend for libretro.
|
|
* Copyright (C) 2011-2017 - Daniel De Matteis
|
|
* Copyright (C) 2016-2019 - Brad Parker
|
|
* Copyright (C) 2016-2019 - Andrés Suárez
|
|
*
|
|
* RetroArch is free software: you can redistribute it and/or modify it under the terms
|
|
* of the GNU General Public License as published by the Free Software Found-
|
|
* ation, either version 3 of the License, or (at your option) any later version.
|
|
*
|
|
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
* PURPOSE. See the GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along with RetroArch.
|
|
* If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/* TODO/FIXME - turn this into actual task */
|
|
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <sys/types.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <errno.h>
|
|
|
|
#include <file/file_path.h>
|
|
#include <queues/task_queue.h>
|
|
#include <string/stdstring.h>
|
|
|
|
#ifdef _WIN32
|
|
#ifdef _XBOX
|
|
#include <xtl.h>
|
|
#define setmode _setmode
|
|
#define INVALID_FILE_ATTRIBUTES -1
|
|
#else
|
|
#include <io.h>
|
|
#include <fcntl.h>
|
|
#include <windows.h>
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef __WINRT__
|
|
#include <uwp/uwp_func.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "../config.h"
|
|
#endif
|
|
|
|
#include <boolean.h>
|
|
|
|
#include <encodings/crc32.h>
|
|
#include <compat/strl.h>
|
|
#include <compat/posix_string.h>
|
|
#include <file/file_path.h>
|
|
#include <file/archive_file.h>
|
|
#include <string/stdstring.h>
|
|
|
|
#include <vfs/vfs_implementation.h>
|
|
#ifdef HAVE_CDROM
|
|
#include <vfs/vfs_implementation_cdrom.h>
|
|
#endif
|
|
|
|
#include <retro_miscellaneous.h>
|
|
#include <streams/file_stream.h>
|
|
#include <retro_assert.h>
|
|
|
|
#include <lists/string_list.h>
|
|
#include <string/stdstring.h>
|
|
|
|
#ifdef HAVE_MENU
|
|
#include "../menu/menu_driver.h"
|
|
#endif
|
|
|
|
#ifdef HAVE_GFX_WIDGETS
|
|
#include "../gfx/gfx_widgets.h"
|
|
#endif
|
|
|
|
#if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
|
|
#include "../menu/menu_shader.h"
|
|
#endif
|
|
|
|
#ifdef HAVE_CHEEVOS
|
|
#include "../cheevos/cheevos.h"
|
|
#endif
|
|
|
|
#include "task_content.h"
|
|
#include "tasks_internal.h"
|
|
|
|
#include "../command.h"
|
|
#include "../core_info.h"
|
|
#include "../content.h"
|
|
#include "../configuration.h"
|
|
#include "../defaults.h"
|
|
#include "../frontend/frontend.h"
|
|
#include "../playlist.h"
|
|
#include "../paths.h"
|
|
#include "../retroarch.h"
|
|
#include "../verbosity.h"
|
|
|
|
#include "../msg_hash.h"
|
|
#include "../content.h"
|
|
#include "../dynamic.h"
|
|
#include "../retroarch.h"
|
|
#include "../file_path_special.h"
|
|
#include "../core.h"
|
|
#include "../paths.h"
|
|
#include "../verbosity.h"
|
|
|
|
#include "../network/discord.h"
|
|
|
|
/* TODO/FIXME - get rid of this public global */
|
|
extern bool discord_is_inited;
|
|
|
|
#define MAX_ARGS 32
|
|
|
|
typedef struct content_stream content_stream_t;
|
|
typedef struct content_information_ctx content_information_ctx_t;
|
|
|
|
#ifdef HAVE_CDROM
|
|
enum cdrom_dump_state
|
|
{
|
|
DUMP_STATE_TOC_PENDING = 0,
|
|
DUMP_STATE_WRITE_CUE,
|
|
DUMP_STATE_NEXT_TRACK,
|
|
DUMP_STATE_READ_TRACK
|
|
};
|
|
|
|
typedef struct
|
|
{
|
|
int64_t cur_track_bytes;
|
|
int64_t track_written_bytes;
|
|
int64_t disc_total_bytes;
|
|
int64_t disc_read_bytes;
|
|
|
|
RFILE *file;
|
|
RFILE *output_file;
|
|
libretro_vfs_implementation_file *stream;
|
|
const cdrom_toc_t *toc;
|
|
|
|
enum cdrom_dump_state state;
|
|
unsigned char cur_track;
|
|
char drive_letter[2];
|
|
char cdrom_path[64];
|
|
char title[512];
|
|
bool next;
|
|
} task_cdrom_dump_state_t;
|
|
#endif
|
|
|
|
struct content_stream
|
|
{
|
|
const uint8_t *b;
|
|
size_t c;
|
|
uint32_t a;
|
|
uint32_t crc;
|
|
};
|
|
|
|
struct content_information_ctx
|
|
{
|
|
char *name_ips;
|
|
char *name_bps;
|
|
char *name_ups;
|
|
|
|
char *valid_extensions;
|
|
char *directory_cache;
|
|
char *directory_system;
|
|
|
|
struct string_list *temporary_content;
|
|
|
|
struct
|
|
{
|
|
struct retro_subsystem_info *data;
|
|
unsigned size;
|
|
} subsystem;
|
|
|
|
bool block_extract;
|
|
bool need_fullpath;
|
|
bool set_supports_no_game_enable;
|
|
#ifdef HAVE_PATCH
|
|
bool is_ips_pref;
|
|
bool is_bps_pref;
|
|
bool is_ups_pref;
|
|
bool patch_is_blocked;
|
|
#endif
|
|
bool bios_is_missing;
|
|
bool check_firmware_before_loading;
|
|
};
|
|
|
|
#ifdef HAVE_CDROM
|
|
static void task_cdrom_dump_handler(retro_task_t *task)
|
|
{
|
|
task_cdrom_dump_state_t *state = (task_cdrom_dump_state_t*)task->state;
|
|
settings_t *settings = config_get_ptr();
|
|
const char *directory_core_assets = settings
|
|
? settings->paths.directory_core_assets : NULL;
|
|
|
|
if (task_get_progress(task) == 100)
|
|
{
|
|
if (state->file)
|
|
filestream_close(state->file);
|
|
if (state->output_file)
|
|
filestream_close(state->output_file);
|
|
state->file = NULL;
|
|
state->output_file = NULL;
|
|
|
|
task_set_finished(task, true);
|
|
|
|
RARCH_LOG("[CDROM]: Dump finished.\n");
|
|
|
|
return;
|
|
}
|
|
|
|
switch (state->state)
|
|
{
|
|
case DUMP_STATE_TOC_PENDING:
|
|
{
|
|
/* open cuesheet file from drive */
|
|
char cue_path[PATH_MAX_LENGTH] = {0};
|
|
|
|
cdrom_device_fillpath(cue_path, sizeof(cue_path), state->drive_letter[0], 0, true);
|
|
|
|
state->file = filestream_open(cue_path, RETRO_VFS_FILE_ACCESS_READ, 0);
|
|
|
|
if (!state->file)
|
|
{
|
|
RARCH_ERR("[CDROM]: Error opening file for reading: %s\n", cue_path);
|
|
task_set_progress(task, 100);
|
|
task_free_title(task);
|
|
task_set_title(task, strdup(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_FILE_READ_OPEN_FAILED)));
|
|
return;
|
|
}
|
|
|
|
state->state = DUMP_STATE_WRITE_CUE;
|
|
|
|
break;
|
|
}
|
|
case DUMP_STATE_WRITE_CUE:
|
|
{
|
|
char output_file[PATH_MAX_LENGTH];
|
|
char cue_filename[PATH_MAX_LENGTH];
|
|
/* write cuesheet to a file */
|
|
int64_t cue_size = filestream_get_size(state->file);
|
|
char *cue_data = (char*)calloc(1, cue_size);
|
|
|
|
output_file[0] = cue_filename[0] = '\0';
|
|
|
|
filestream_read(state->file, cue_data, cue_size);
|
|
|
|
state->stream = filestream_get_vfs_handle(state->file);
|
|
state->toc = retro_vfs_file_get_cdrom_toc();
|
|
|
|
if (cdrom_has_atip(state->stream))
|
|
RARCH_LOG("[CDROM]: This disc is not genuine.\n");
|
|
|
|
filestream_close(state->file);
|
|
|
|
snprintf(cue_filename, sizeof(cue_filename), "%s.cue", state->title);
|
|
|
|
fill_pathname_join(output_file,
|
|
directory_core_assets, cue_filename, sizeof(output_file));
|
|
|
|
{
|
|
RFILE *file = filestream_open(output_file, RETRO_VFS_FILE_ACCESS_WRITE, 0);
|
|
unsigned char point = 0;
|
|
|
|
if (!file)
|
|
{
|
|
RARCH_ERR("[CDROM]: Error opening file for writing: %s\n", output_file);
|
|
task_set_progress(task, 100);
|
|
task_free_title(task);
|
|
task_set_title(task, strdup(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_FILE_WRITE_OPEN_FAILED)));
|
|
return;
|
|
}
|
|
|
|
for (point = 1; point <= state->toc->num_tracks; point++)
|
|
{
|
|
const char *track_type = "MODE1/2352";
|
|
char track_filename[PATH_MAX_LENGTH];
|
|
|
|
state->disc_total_bytes += state->toc->track[point - 1].track_bytes;
|
|
|
|
track_filename[0] = '\0';
|
|
|
|
if (state->toc->track[point - 1].audio)
|
|
track_type = "AUDIO";
|
|
else if (state->toc->track[point - 1].mode == 1)
|
|
track_type = "MODE1/2352";
|
|
else if (state->toc->track[point - 1].mode == 2)
|
|
track_type = "MODE2/2352";
|
|
|
|
snprintf(track_filename, sizeof(track_filename), "%s (Track %02d).bin", state->title, point);
|
|
|
|
filestream_printf(file, "FILE \"%s\" BINARY\n", track_filename);
|
|
filestream_printf(file, " TRACK %02d %s\n", point, track_type);
|
|
|
|
{
|
|
unsigned pregap_lba_len = state->toc->track[point - 1].lba - state->toc->track[point - 1].lba_start;
|
|
|
|
if (state->toc->track[point - 1].audio && pregap_lba_len > 0)
|
|
{
|
|
unsigned char min = 0;
|
|
unsigned char sec = 0;
|
|
unsigned char frame = 0;
|
|
|
|
cdrom_lba_to_msf(pregap_lba_len, &min, &sec, &frame);
|
|
|
|
filestream_printf(file, " INDEX 00 00:00:00\n");
|
|
filestream_printf(file, " INDEX 01 %02u:%02u:%02u\n", (unsigned)min, (unsigned)sec, (unsigned)frame);
|
|
}
|
|
else
|
|
filestream_printf(file, " INDEX 01 00:00:00\n");
|
|
}
|
|
}
|
|
|
|
filestream_close(file);
|
|
}
|
|
|
|
state->file = NULL;
|
|
state->state = DUMP_STATE_NEXT_TRACK;
|
|
|
|
free(cue_data);
|
|
}
|
|
break;
|
|
case DUMP_STATE_NEXT_TRACK:
|
|
{
|
|
/* no file is open as we either just started or just finished a track, need to start dumping the next track */
|
|
state->cur_track++;
|
|
|
|
/* no more tracks to dump, we're done */
|
|
if (state->toc && state->cur_track > state->toc->num_tracks)
|
|
{
|
|
task_set_progress(task, 100);
|
|
return;
|
|
}
|
|
|
|
RARCH_LOG("[CDROM]: Dumping track %d...\n", state->cur_track);
|
|
|
|
memset(state->cdrom_path, 0, sizeof(state->cdrom_path));
|
|
|
|
cdrom_device_fillpath(state->cdrom_path, sizeof(state->cdrom_path), state->drive_letter[0], state->cur_track, false);
|
|
|
|
state->track_written_bytes = 0;
|
|
state->file = filestream_open(state->cdrom_path, RETRO_VFS_FILE_ACCESS_READ, 0);
|
|
|
|
/* open a new file for writing for this next track */
|
|
if (state->file)
|
|
{
|
|
char output_path[PATH_MAX_LENGTH];
|
|
char track_filename[PATH_MAX_LENGTH];
|
|
|
|
output_path[0] = track_filename[0] = '\0';
|
|
|
|
snprintf(track_filename, sizeof(track_filename), "%s (Track %02d).bin", state->title, state->cur_track);
|
|
|
|
state->cur_track_bytes = filestream_get_size(state->file);
|
|
|
|
fill_pathname_join(output_path,
|
|
directory_core_assets, track_filename, sizeof(output_path));
|
|
|
|
state->output_file = filestream_open(output_path, RETRO_VFS_FILE_ACCESS_WRITE, 0);
|
|
|
|
if (!state->output_file)
|
|
{
|
|
RARCH_ERR("[CDROM]: Error opening file for writing: %s\n", output_path);
|
|
task_set_progress(task, 100);
|
|
task_free_title(task);
|
|
task_set_title(task, strdup(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_FILE_WRITE_OPEN_FAILED)));
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RARCH_ERR("[CDROM]: Error opening file for writing: %s\n", state->cdrom_path);
|
|
task_set_progress(task, 100);
|
|
task_free_title(task);
|
|
task_set_title(task, strdup(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_FILE_WRITE_OPEN_FAILED)));
|
|
return;
|
|
}
|
|
|
|
state->state = DUMP_STATE_READ_TRACK;
|
|
}
|
|
break;
|
|
case DUMP_STATE_READ_TRACK:
|
|
/* read data from track and write it to a file in chunks */
|
|
if (state->cur_track_bytes > state->track_written_bytes)
|
|
{
|
|
char data[2352 * 2] = {0};
|
|
int64_t read_bytes = filestream_read(state->file, data, sizeof(data));
|
|
int progress = 0;
|
|
|
|
if (read_bytes <= 0)
|
|
{
|
|
task_set_progress(task, 100);
|
|
task_free_title(task);
|
|
task_set_title(task, strdup(msg_hash_to_str(MSG_DISC_DUMP_FAILED_TO_READ_FROM_DRIVE)));
|
|
return;
|
|
}
|
|
|
|
state->track_written_bytes += read_bytes;
|
|
state->disc_read_bytes += read_bytes;
|
|
progress = (state->disc_read_bytes / (double)state->disc_total_bytes) * 100.0;
|
|
|
|
#ifdef CDROM_DEBUG
|
|
RARCH_LOG("[CDROM]: Read %" PRId64 " bytes, totalling %" PRId64 " of %" PRId64 " bytes. Progress: %d%%\n", read_bytes, state->track_written_bytes, state->cur_track_bytes, progress);
|
|
#endif
|
|
|
|
if (filestream_write(state->output_file, data, read_bytes) <= 0)
|
|
{
|
|
task_set_progress(task, 100);
|
|
task_free_title(task);
|
|
task_set_title(task, strdup(msg_hash_to_str(MSG_DISC_DUMP_FAILED_TO_WRITE_TO_DISK)));
|
|
return;
|
|
}
|
|
|
|
task_set_progress(task, progress);
|
|
}
|
|
else if (state->cur_track_bytes == state->track_written_bytes)
|
|
{
|
|
/* TODO: FIXME: this stops after only the first track */
|
|
if (state->file)
|
|
{
|
|
filestream_close(state->file);
|
|
state->file = NULL;
|
|
}
|
|
if (state->output_file)
|
|
{
|
|
filestream_close(state->output_file);
|
|
state->file = NULL;
|
|
}
|
|
|
|
state->state = DUMP_STATE_NEXT_TRACK;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void task_cdrom_dump_callback(retro_task_t *task,
|
|
void *task_data,
|
|
void *user_data, const char *error)
|
|
{
|
|
task_cdrom_dump_state_t *state = (task_cdrom_dump_state_t*)task->state;
|
|
|
|
if (state)
|
|
free(state);
|
|
}
|
|
|
|
void task_push_cdrom_dump(const char *drive)
|
|
{
|
|
retro_task_t *task = task_init();
|
|
task_cdrom_dump_state_t *state = (task_cdrom_dump_state_t*)calloc(1, sizeof(*state));
|
|
|
|
state->drive_letter[0] = drive[0];
|
|
state->next = true;
|
|
state->cur_track = 0;
|
|
state->state = DUMP_STATE_TOC_PENDING;
|
|
|
|
fill_str_dated_filename(state->title, "cdrom", NULL, sizeof(state->title));
|
|
|
|
task->state = state;
|
|
task->handler = task_cdrom_dump_handler;
|
|
task->callback = task_cdrom_dump_callback;
|
|
task->title = strdup(msg_hash_to_str(MSG_DUMPING_DISC));
|
|
|
|
RARCH_LOG("[CDROM]: Starting disc dump...\n");
|
|
|
|
task_queue_push(task);
|
|
}
|
|
#endif
|
|
|
|
static int64_t content_file_read(const char *path, void **buf, int64_t *length)
|
|
{
|
|
#ifdef HAVE_COMPRESSION
|
|
if ( path_contains_compressed_file(path)
|
|
&& file_archive_compressed_read(path, buf, NULL, length))
|
|
return 1;
|
|
#endif
|
|
return filestream_read_file(path, buf, length);
|
|
}
|
|
|
|
/**
|
|
* content_load_init_wrap:
|
|
* @args : Input arguments.
|
|
* @argc : Count of arguments.
|
|
* @argv : Arguments.
|
|
*
|
|
* Generates an @argc and @argv pair based on @args
|
|
* of type rarch_main_wrap.
|
|
**/
|
|
static void content_load_init_wrap(
|
|
const struct rarch_main_wrap *args,
|
|
int *argc, char **argv,
|
|
bool print_args)
|
|
{
|
|
*argc = 0;
|
|
argv[(*argc)++] = strdup("retroarch");
|
|
|
|
if (args->content_path)
|
|
{
|
|
RARCH_LOG("[CORE]: Using content: %s.\n", args->content_path);
|
|
argv[(*argc)++] = strdup(args->content_path);
|
|
}
|
|
#ifdef HAVE_MENU
|
|
else
|
|
{
|
|
RARCH_LOG("[CORE]: %s\n",
|
|
msg_hash_to_str(MSG_NO_CONTENT_STARTING_DUMMY_CORE));
|
|
argv[(*argc)++] = strdup("--menu");
|
|
}
|
|
#endif
|
|
|
|
if (args->sram_path)
|
|
{
|
|
argv[(*argc)++] = strdup("-s");
|
|
argv[(*argc)++] = strdup(args->sram_path);
|
|
}
|
|
|
|
if (args->state_path)
|
|
{
|
|
argv[(*argc)++] = strdup("-S");
|
|
argv[(*argc)++] = strdup(args->state_path);
|
|
}
|
|
|
|
if (args->config_path)
|
|
{
|
|
argv[(*argc)++] = strdup("-c");
|
|
argv[(*argc)++] = strdup(args->config_path);
|
|
}
|
|
|
|
#ifdef HAVE_DYNAMIC
|
|
if (args->libretro_path)
|
|
{
|
|
argv[(*argc)++] = strdup("-L");
|
|
argv[(*argc)++] = strdup(args->libretro_path);
|
|
}
|
|
#endif
|
|
|
|
if (args->verbose)
|
|
argv[(*argc)++] = strdup("-v");
|
|
|
|
if (print_args)
|
|
{
|
|
int i;
|
|
for (i = 0; i < *argc; i++)
|
|
RARCH_LOG("[CORE]: Arg #%d: %s\n", i, argv[i]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* content_load:
|
|
*
|
|
* Loads content file and starts up RetroArch.
|
|
* If no content file can be loaded, will start up RetroArch
|
|
* as-is.
|
|
*
|
|
* Returns: false (0) if retroarch_main_init failed,
|
|
* otherwise true (1).
|
|
**/
|
|
static bool content_load(content_ctx_info_t *info,
|
|
content_state_t *p_content)
|
|
{
|
|
unsigned i = 0;
|
|
bool success = false;
|
|
int rarch_argc = 0;
|
|
char *rarch_argv[MAX_ARGS] = {NULL};
|
|
char *argv_copy [MAX_ARGS] = {NULL};
|
|
char **rarch_argv_ptr = (char**)info->argv;
|
|
int *rarch_argc_ptr = (int*)&info->argc;
|
|
struct rarch_main_wrap *wrap_args = NULL;
|
|
|
|
if (!(wrap_args = (struct rarch_main_wrap*)
|
|
malloc(sizeof(*wrap_args))))
|
|
return false;
|
|
|
|
retro_assert(wrap_args);
|
|
|
|
wrap_args->argv = NULL;
|
|
wrap_args->content_path = NULL;
|
|
wrap_args->sram_path = NULL;
|
|
wrap_args->state_path = NULL;
|
|
wrap_args->config_path = NULL;
|
|
wrap_args->libretro_path = NULL;
|
|
wrap_args->verbose = false;
|
|
wrap_args->no_content = false;
|
|
wrap_args->touched = false;
|
|
wrap_args->argc = 0;
|
|
|
|
if (info->environ_get)
|
|
info->environ_get(rarch_argc_ptr,
|
|
rarch_argv_ptr, info->args, wrap_args);
|
|
|
|
if (wrap_args->touched)
|
|
{
|
|
content_load_init_wrap(wrap_args, &rarch_argc, rarch_argv,
|
|
false);
|
|
memcpy(argv_copy, rarch_argv, sizeof(rarch_argv));
|
|
rarch_argv_ptr = (char**)rarch_argv;
|
|
rarch_argc_ptr = (int*)&rarch_argc;
|
|
}
|
|
|
|
rarch_ctl(RARCH_CTL_MAIN_DEINIT, NULL);
|
|
|
|
wrap_args->argc = *rarch_argc_ptr;
|
|
wrap_args->argv = rarch_argv_ptr;
|
|
|
|
success = retroarch_main_init(wrap_args->argc, wrap_args->argv);
|
|
|
|
for (i = 0; i < ARRAY_SIZE(argv_copy); i++)
|
|
free(argv_copy[i]);
|
|
free(wrap_args);
|
|
|
|
if (!success)
|
|
return false;
|
|
|
|
if (p_content->pending_subsystem_init)
|
|
{
|
|
command_event(CMD_EVENT_CORE_INIT, NULL);
|
|
content_clear_subsystem();
|
|
}
|
|
|
|
#ifdef HAVE_GFX_WIDGETS
|
|
/* If retroarch_main_init() returned true, we
|
|
* can safely trigger a load content animation */
|
|
if (gfx_widgets_ready())
|
|
{
|
|
/* Note: Have to read settings value here
|
|
* (It will be invalid if we try to read
|
|
* it earlier...) */
|
|
#ifdef HAVE_CONFIGFILE
|
|
settings_t *settings = config_get_ptr();
|
|
bool show_load_content_animation = settings && settings->bools.menu_show_load_content_animation;
|
|
#else
|
|
bool show_load_content_animation = false;
|
|
#endif
|
|
|
|
if (show_load_content_animation)
|
|
gfx_widget_start_load_content_animation();
|
|
}
|
|
#endif
|
|
|
|
#if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
|
|
menu_shader_manager_init();
|
|
#endif
|
|
|
|
command_event(CMD_EVENT_HISTORY_INIT, NULL);
|
|
rarch_favorites_init();
|
|
command_event(CMD_EVENT_RESUME, NULL);
|
|
command_event(CMD_EVENT_VIDEO_SET_ASPECT_RATIO, NULL);
|
|
|
|
dir_check_defaults();
|
|
|
|
frontend_driver_process_args(rarch_argc_ptr, rarch_argv_ptr);
|
|
frontend_driver_content_loaded();
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* load_content_into_memory:
|
|
* @path : buffer of the content file.
|
|
* @buf : size of the content file.
|
|
* @length : size of the content file that has been read from.
|
|
*
|
|
* Read the content file. If read into memory, also performs soft patching
|
|
* (see patch_content function) in case soft patching has not been
|
|
* blocked by the enduser.
|
|
*
|
|
* Returns: true if successful, false on error.
|
|
**/
|
|
static bool load_content_into_memory(
|
|
content_information_ctx_t *content_ctx,
|
|
content_state_t *p_content,
|
|
unsigned i, const char *path, void **buf,
|
|
int64_t *length)
|
|
{
|
|
uint8_t *ret_buf = NULL;
|
|
|
|
RARCH_LOG("[CONTENT LOAD]: %s: %s.\n",
|
|
msg_hash_to_str(MSG_LOADING_CONTENT_FILE), path);
|
|
|
|
if (!content_file_read(path, (void**) &ret_buf, length))
|
|
return false;
|
|
|
|
if (*length < 0)
|
|
return false;
|
|
|
|
if (i == 0)
|
|
{
|
|
enum rarch_content_type type = path_is_media_type(path);
|
|
|
|
/* If we have a media type, ignore CRC32 calculation. */
|
|
if (type == RARCH_CONTENT_NONE)
|
|
{
|
|
#ifdef HAVE_PATCH
|
|
bool has_patch = false;
|
|
|
|
/* First content file is significant, attempt to do patching,
|
|
* CRC checking, etc. */
|
|
|
|
/* Attempt to apply a patch. */
|
|
if (!content_ctx->patch_is_blocked)
|
|
has_patch = patch_content(
|
|
content_ctx->is_ips_pref,
|
|
content_ctx->is_bps_pref,
|
|
content_ctx->is_ups_pref,
|
|
content_ctx->name_ips,
|
|
content_ctx->name_bps,
|
|
content_ctx->name_ups,
|
|
(uint8_t**)&ret_buf,
|
|
(void*)length);
|
|
|
|
if (has_patch)
|
|
{
|
|
p_content->rom_crc = encoding_crc32(0, ret_buf, (size_t)*length);
|
|
RARCH_LOG("[CONTENT LOAD]: CRC32: 0x%x .\n", (unsigned)p_content->rom_crc);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
strlcpy(p_content->pending_rom_crc_path,
|
|
path, sizeof(p_content->pending_rom_crc_path));
|
|
p_content->pending_rom_crc = true;
|
|
}
|
|
}
|
|
else
|
|
p_content->rom_crc = 0;
|
|
}
|
|
|
|
*buf = ret_buf;
|
|
|
|
return true;
|
|
}
|
|
|
|
#ifdef HAVE_COMPRESSION
|
|
static bool load_content_from_compressed_archive(
|
|
content_information_ctx_t *content_ctx,
|
|
struct retro_game_info *info,
|
|
unsigned i,
|
|
struct string_list* additional_path_allocs,
|
|
bool need_fullpath,
|
|
const char *path,
|
|
char **error_string)
|
|
{
|
|
union string_list_elem_attr attr;
|
|
int64_t new_path_len = 0;
|
|
char new_basedir[PATH_MAX_LENGTH];
|
|
char new_path[PATH_MAX_LENGTH];
|
|
|
|
new_path[0] = '\0';
|
|
new_basedir[0] = '\0';
|
|
attr.i = 0;
|
|
|
|
RARCH_LOG("[CONTENT LOAD]: Compressed file in case of need_fullpath."
|
|
" Now extracting to temporary directory.\n");
|
|
|
|
if (!string_is_empty(content_ctx->directory_cache))
|
|
strlcpy(new_basedir, content_ctx->directory_cache,
|
|
sizeof(new_basedir));
|
|
|
|
if (!path_is_directory(new_basedir))
|
|
{
|
|
RARCH_WARN("[CONTENT LOAD]: Tried extracting to cache directory, but "
|
|
"cache directory was not set or found. "
|
|
"Setting cache directory to directory "
|
|
"derived by basename...\n");
|
|
fill_pathname_basedir(new_basedir, path, sizeof(new_basedir));
|
|
}
|
|
|
|
fill_pathname_join(new_path, new_basedir,
|
|
path_basename(path), sizeof(new_path));
|
|
|
|
if (!file_archive_compressed_read(path,
|
|
NULL, new_path, &new_path_len) || new_path_len < 0)
|
|
{
|
|
char str[1024];
|
|
str[0] = '\0';
|
|
snprintf(str, sizeof(str),
|
|
"%s \"%s\".\n",
|
|
msg_hash_to_str(MSG_COULD_NOT_READ_CONTENT_FILE),
|
|
path);
|
|
*error_string = strdup(str);
|
|
return false;
|
|
}
|
|
|
|
string_list_append(additional_path_allocs, new_path, attr);
|
|
info[i].path =
|
|
additional_path_allocs->elems[additional_path_allocs->size - 1].data;
|
|
|
|
return string_list_append(content_ctx->temporary_content,
|
|
new_path, attr);
|
|
}
|
|
|
|
/* Try to extract all content we're going to load if appropriate. */
|
|
|
|
static bool content_file_init_extract(
|
|
struct string_list *content,
|
|
content_information_ctx_t *content_ctx,
|
|
const struct retro_subsystem_info *special,
|
|
char **error_string,
|
|
union string_list_elem_attr *attr
|
|
)
|
|
{
|
|
unsigned i;
|
|
|
|
for (i = 0; i < content->size; i++)
|
|
{
|
|
bool block_extract = content->elems[i].attr.i & 1;
|
|
const char *path = content->elems[i].data;
|
|
bool contains_compressed = path_contains_compressed_file(path);
|
|
|
|
/* Block extract check. */
|
|
if (block_extract)
|
|
continue;
|
|
|
|
/* just use the first file in the archive */
|
|
if (!contains_compressed && !path_is_compressed_file(path))
|
|
continue;
|
|
|
|
{
|
|
char temp_content[PATH_MAX_LENGTH];
|
|
char new_path [PATH_MAX_LENGTH];
|
|
const char *valid_ext = special ?
|
|
special->roms[i].valid_extensions :
|
|
content_ctx->valid_extensions;
|
|
|
|
temp_content[0] = new_path[0] = '\0';
|
|
|
|
if (!string_is_empty(path))
|
|
strlcpy(temp_content, path, sizeof(temp_content));
|
|
|
|
if (!valid_ext || !file_archive_extract_file(
|
|
temp_content,
|
|
sizeof(temp_content),
|
|
valid_ext,
|
|
!string_is_empty(content_ctx->directory_cache) ?
|
|
content_ctx->directory_cache : NULL,
|
|
new_path,
|
|
sizeof(new_path)
|
|
))
|
|
{
|
|
char str[1024];
|
|
str[0] = '\0';
|
|
|
|
snprintf(str, sizeof(str),
|
|
"%s: %s.\n",
|
|
msg_hash_to_str(
|
|
MSG_FAILED_TO_EXTRACT_CONTENT_FROM_COMPRESSED_FILE),
|
|
temp_content);
|
|
*error_string = strdup(str);
|
|
return false;
|
|
}
|
|
|
|
string_list_set(content, i, new_path);
|
|
|
|
if (!string_list_append(
|
|
content_ctx->temporary_content,
|
|
new_path, *attr))
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* content_file_load:
|
|
* @special : subsystem of content to be loaded. Can be NULL.
|
|
* content :
|
|
*
|
|
* Load content file (for libretro core).
|
|
*
|
|
* Returns : true if successful, otherwise false.
|
|
**/
|
|
static bool content_file_load(
|
|
struct retro_game_info *info,
|
|
content_state_t *p_content,
|
|
const struct string_list *content,
|
|
content_information_ctx_t *content_ctx,
|
|
char **error_string,
|
|
const struct retro_subsystem_info *special,
|
|
struct string_list *additional_path_allocs
|
|
)
|
|
{
|
|
unsigned i;
|
|
retro_ctx_load_content_info_t load_info;
|
|
bool used_vfs_fallback_copy = false;
|
|
#ifdef __WINRT__
|
|
rarch_system_info_t *system = runloop_get_system_info();
|
|
#endif
|
|
|
|
for (i = 0; i < content->size; i++)
|
|
{
|
|
int attr = content->elems[i].attr.i;
|
|
const char *path = content->elems[i].data;
|
|
bool need_fullpath = attr & 2;
|
|
bool require_content = attr & 4;
|
|
bool path_empty = string_is_empty(path);
|
|
|
|
if (require_content && path_empty)
|
|
{
|
|
*error_string = strdup(msg_hash_to_str(MSG_ERROR_LIBRETRO_CORE_REQUIRES_CONTENT));
|
|
return false;
|
|
}
|
|
|
|
info[i].path = NULL;
|
|
|
|
if (!path_empty)
|
|
info[i].path = path;
|
|
|
|
if (!need_fullpath && !path_empty)
|
|
{
|
|
/* Load the content into memory. */
|
|
|
|
int64_t len = 0;
|
|
|
|
if (!load_content_into_memory(
|
|
content_ctx, p_content,
|
|
i, path, (void**)&info[i].data, &len))
|
|
{
|
|
char msg[1024];
|
|
msg[0] = '\0';
|
|
|
|
snprintf(msg, sizeof(msg),
|
|
"%s \"%s\".\n",
|
|
msg_hash_to_str(MSG_COULD_NOT_READ_CONTENT_FILE),
|
|
path);
|
|
*error_string = strdup(msg);
|
|
return false;
|
|
}
|
|
|
|
info[i].size = len;
|
|
}
|
|
else
|
|
{
|
|
#ifdef HAVE_COMPRESSION
|
|
if ( !content_ctx->block_extract
|
|
&& need_fullpath
|
|
&& path_contains_compressed_file(path)
|
|
&& !load_content_from_compressed_archive(
|
|
content_ctx,
|
|
&info[i], i,
|
|
additional_path_allocs, need_fullpath, path,
|
|
error_string))
|
|
return false;
|
|
#endif
|
|
|
|
#ifdef __WINRT__
|
|
/* TODO: When support for the 'actual' VFS is added, there will need to be some more logic here */
|
|
if (!system->supports_vfs && !is_path_accessible_using_standard_io(path))
|
|
{
|
|
/* Fallback to a file copy into an accessible directory */
|
|
char* buf;
|
|
int64_t len;
|
|
union string_list_elem_attr attr;
|
|
char new_basedir[PATH_MAX_LENGTH];
|
|
char new_path[PATH_MAX_LENGTH];
|
|
|
|
new_path[0] = '\0';
|
|
new_basedir[0] = '\0';
|
|
attr.i = 0;
|
|
|
|
RARCH_LOG("[CONTENT LOAD]: Core does not support VFS - copying to cache directory\n");
|
|
|
|
if (!string_is_empty(content_ctx->directory_cache))
|
|
strlcpy(new_basedir, content_ctx->directory_cache, sizeof(new_basedir));
|
|
if ( string_is_empty(new_basedir) ||
|
|
!path_is_directory(new_basedir) ||
|
|
!is_path_accessible_using_standard_io(new_basedir))
|
|
{
|
|
RARCH_WARN("[CONTENT LOAD]: Tried copying to cache directory"
|
|
", but "
|
|
"cache directory was not set or found. "
|
|
"Setting cache directory to root of "
|
|
"writable app directory...\n");
|
|
strlcpy(new_basedir, uwp_dir_data, sizeof(new_basedir));
|
|
}
|
|
|
|
fill_pathname_join(new_path, new_basedir,
|
|
path_basename(path), sizeof(new_path));
|
|
|
|
/* TODO: This may fail on very large files...
|
|
* but copying large files is not a good idea anyway */
|
|
if (!filestream_read_file(path, &buf, &len))
|
|
{
|
|
char msg[1024];
|
|
msg[0] = '\0';
|
|
|
|
snprintf(msg,
|
|
sizeof(msg),
|
|
"%s \"%s\". (during copy read)\n",
|
|
msg_hash_to_str(MSG_COULD_NOT_READ_CONTENT_FILE),
|
|
path);
|
|
*error_string = strdup(msg);
|
|
return false;
|
|
}
|
|
|
|
if (!filestream_write_file(new_path, buf, len))
|
|
{
|
|
char msg[1024];
|
|
msg[0] = '\0';
|
|
|
|
free(buf);
|
|
snprintf(msg,
|
|
sizeof(msg),
|
|
"%s \"%s\". (during copy write)\n",
|
|
msg_hash_to_str(MSG_COULD_NOT_READ_CONTENT_FILE),
|
|
path);
|
|
*error_string = strdup(msg);
|
|
return false;
|
|
}
|
|
|
|
free(buf);
|
|
|
|
string_list_append(additional_path_allocs, new_path, attr);
|
|
info[i].path =
|
|
additional_path_allocs->elems[additional_path_allocs->size - 1].data;
|
|
|
|
string_list_append(content_ctx->temporary_content,
|
|
new_path, attr);
|
|
|
|
used_vfs_fallback_copy = true;
|
|
}
|
|
#endif
|
|
|
|
RARCH_LOG("[CONTENT LOAD]: %s\n", msg_hash_to_str(
|
|
MSG_CONTENT_LOADING_SKIPPED_IMPLEMENTATION_WILL_DO_IT));
|
|
strlcpy(p_content->pending_rom_crc_path,
|
|
path, sizeof(p_content->pending_rom_crc_path));
|
|
p_content->pending_rom_crc = true;
|
|
}
|
|
}
|
|
|
|
load_info.content = content;
|
|
load_info.special = special;
|
|
load_info.info = info;
|
|
|
|
if (!core_load_game(&load_info))
|
|
{
|
|
/* This is probably going to fail on multifile ROMs etc.
|
|
* so give a visible explanation of what is likely wrong */
|
|
if (used_vfs_fallback_copy)
|
|
*error_string = strdup(msg_hash_to_str(
|
|
MSG_ERROR_LIBRETRO_CORE_REQUIRES_VFS));
|
|
else
|
|
*error_string = strdup(msg_hash_to_str(
|
|
MSG_FAILED_TO_LOAD_CONTENT));
|
|
|
|
return false;
|
|
}
|
|
|
|
#ifdef HAVE_CHEEVOS
|
|
if (!special)
|
|
{
|
|
const char *content_path = content->elems[0].data;
|
|
enum rarch_content_type type = path_is_media_type(content_path);
|
|
|
|
if (type == RARCH_CONTENT_NONE && !string_is_empty(content_path))
|
|
rcheevos_load(info);
|
|
else
|
|
rcheevos_pause_hardcore();
|
|
}
|
|
else
|
|
rcheevos_pause_hardcore();
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
static const struct
|
|
retro_subsystem_info *content_file_init_subsystem(
|
|
const struct retro_subsystem_info *subsystem_data,
|
|
size_t subsystem_current_count,
|
|
char **error_string,
|
|
bool *ret)
|
|
{
|
|
struct string_list *subsystem = path_get_subsystem_list();
|
|
const struct retro_subsystem_info *special = libretro_find_subsystem_info(
|
|
subsystem_data, (unsigned)subsystem_current_count,
|
|
path_get(RARCH_PATH_SUBSYSTEM));
|
|
|
|
if (!special)
|
|
{
|
|
char msg[1024];
|
|
msg[0] = '\0';
|
|
snprintf(msg, sizeof(msg),
|
|
"Failed to find subsystem \"%s\" in libretro implementation.\n",
|
|
path_get(RARCH_PATH_SUBSYSTEM));
|
|
*error_string = strdup(msg);
|
|
goto error;
|
|
}
|
|
|
|
if (special->num_roms)
|
|
{
|
|
if (!subsystem)
|
|
{
|
|
char msg[1024];
|
|
msg[0] = '\0';
|
|
strlcpy(msg,
|
|
msg_hash_to_str(
|
|
MSG_ERROR_LIBRETRO_CORE_REQUIRES_SPECIAL_CONTENT),
|
|
sizeof(msg)
|
|
);
|
|
*error_string = strdup(msg);
|
|
goto error;
|
|
}
|
|
|
|
if (special->num_roms != subsystem->size)
|
|
{
|
|
char msg[1024];
|
|
msg[0] = '\0';
|
|
snprintf(msg,
|
|
sizeof(msg),
|
|
"Libretro core requires %u content files for "
|
|
"subsystem \"%s\", but %u content files were provided.\n",
|
|
special->num_roms, special->desc,
|
|
(unsigned)subsystem->size);
|
|
*error_string = strdup(msg);
|
|
goto error;
|
|
}
|
|
}
|
|
else if (subsystem && subsystem->size)
|
|
{
|
|
char msg[1024];
|
|
msg[0] = '\0';
|
|
snprintf(msg,
|
|
sizeof(msg),
|
|
"Libretro core takes no content for subsystem \"%s\", "
|
|
"but %u content files were provided.\n",
|
|
special->desc,
|
|
(unsigned)subsystem->size);
|
|
*error_string = strdup(msg);
|
|
goto error;
|
|
}
|
|
|
|
*ret = true;
|
|
return special;
|
|
|
|
error:
|
|
*ret = false;
|
|
return NULL;
|
|
}
|
|
|
|
static void content_file_init_set_attribs(
|
|
struct string_list *content,
|
|
const struct retro_subsystem_info *special,
|
|
content_information_ctx_t *content_ctx,
|
|
char **error_string,
|
|
union string_list_elem_attr *attr)
|
|
{
|
|
struct string_list *subsystem = path_get_subsystem_list();
|
|
|
|
attr->i = 0;
|
|
|
|
if (!path_is_empty(RARCH_PATH_SUBSYSTEM) && special)
|
|
{
|
|
unsigned i;
|
|
|
|
for (i = 0; i < subsystem->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(content, subsystem->elems[i].data, *attr);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bool contentless = false;
|
|
bool is_inited = false;
|
|
bool content_path_is_empty = path_is_empty(RARCH_PATH_CONTENT);
|
|
|
|
content_get_status(&contentless, &is_inited);
|
|
|
|
attr->i = content_ctx->block_extract;
|
|
attr->i |= content_ctx->need_fullpath << 1;
|
|
attr->i |= (!contentless) << 2;
|
|
|
|
if (content_path_is_empty
|
|
&& contentless
|
|
&& content_ctx->set_supports_no_game_enable)
|
|
string_list_append(content, "", *attr);
|
|
else if (!content_path_is_empty)
|
|
string_list_append(content, path_get(RARCH_PATH_CONTENT), *attr);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* content_init_file:
|
|
*
|
|
* Initializes and loads a content file for the currently
|
|
* selected libretro core.
|
|
*
|
|
* Returns : true if successful, otherwise false.
|
|
**/
|
|
static bool content_file_init(
|
|
content_information_ctx_t *content_ctx,
|
|
content_state_t *p_content,
|
|
struct string_list *content,
|
|
char **error_string)
|
|
{
|
|
union string_list_elem_attr attr;
|
|
struct retro_game_info *info = NULL;
|
|
bool subsystem_path_is_empty = path_is_empty(RARCH_PATH_SUBSYSTEM);
|
|
bool ret = subsystem_path_is_empty;
|
|
const struct retro_subsystem_info *special =
|
|
subsystem_path_is_empty
|
|
? NULL : content_file_init_subsystem(content_ctx->subsystem.data,
|
|
content_ctx->subsystem.size, error_string, &ret);
|
|
|
|
if (!ret)
|
|
return false;
|
|
|
|
content_file_init_set_attribs(content, special, content_ctx, error_string, &attr);
|
|
#ifdef HAVE_COMPRESSION
|
|
content_file_init_extract(content, content_ctx, special, error_string, &attr);
|
|
#endif
|
|
|
|
if (content->size > 0)
|
|
info = (struct retro_game_info*)
|
|
calloc(content->size, sizeof(*info));
|
|
|
|
if (info)
|
|
{
|
|
unsigned i;
|
|
struct string_list additional_path_allocs;
|
|
|
|
if (string_list_initialize(&additional_path_allocs))
|
|
{
|
|
ret = content_file_load(info, p_content,
|
|
content, content_ctx, error_string,
|
|
special, &additional_path_allocs);
|
|
string_list_deinitialize(&additional_path_allocs);
|
|
}
|
|
|
|
for (i = 0; i < content->size; i++)
|
|
free((void*)info[i].data);
|
|
|
|
free(info);
|
|
}
|
|
else if (!special)
|
|
{
|
|
*error_string = strdup(msg_hash_to_str(MSG_ERROR_LIBRETRO_CORE_REQUIRES_CONTENT));
|
|
ret = false;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void menu_content_environment_get(int *argc, char *argv[],
|
|
void *args, void *params_data);
|
|
|
|
/**
|
|
* task_push_to_history_list:
|
|
*
|
|
* Will push the content entry to the history playlist.
|
|
**/
|
|
static void task_push_to_history_list(
|
|
content_state_t *p_content,
|
|
bool launched_from_menu,
|
|
bool launched_from_cli,
|
|
bool launched_from_companion_ui)
|
|
{
|
|
bool contentless = false;
|
|
bool is_inited = false;
|
|
|
|
content_get_status(&contentless, &is_inited);
|
|
|
|
/* Push entry to top of history playlist */
|
|
if (is_inited || contentless)
|
|
{
|
|
char tmp[PATH_MAX_LENGTH];
|
|
const char *path_content = path_get(RARCH_PATH_CONTENT);
|
|
struct retro_system_info *info = runloop_get_libretro_system_info();
|
|
|
|
tmp[0] = '\0';
|
|
|
|
if (!string_is_empty(path_content))
|
|
strlcpy(tmp, path_content, sizeof(tmp));
|
|
|
|
/* Path can be relative here.
|
|
* Ensure we're pushing absolute path. */
|
|
if (!launched_from_menu && !string_is_empty(tmp))
|
|
path_resolve_realpath(tmp, sizeof(tmp), true);
|
|
|
|
#ifdef HAVE_MENU
|
|
/* Push quick menu onto menu stack */
|
|
if (launched_from_cli)
|
|
menu_driver_ctl(RARCH_MENU_CTL_SET_PENDING_QUICK_MENU, NULL);
|
|
#endif
|
|
|
|
if (info && !string_is_empty(tmp))
|
|
{
|
|
const char *core_path = NULL;
|
|
const char *core_name = NULL;
|
|
const char *label = NULL;
|
|
const char *crc32 = NULL;
|
|
const char *db_name = NULL;
|
|
playlist_t *playlist_hist = g_defaults.content_history;
|
|
settings_t *settings = config_get_ptr();
|
|
global_t *global = global_get_ptr();
|
|
|
|
switch (path_is_media_type(tmp))
|
|
{
|
|
case RARCH_CONTENT_MOVIE:
|
|
#ifdef HAVE_FFMPEG
|
|
playlist_hist = g_defaults.video_history;
|
|
core_name = "movieplayer";
|
|
core_path = "builtin";
|
|
#endif
|
|
break;
|
|
case RARCH_CONTENT_MUSIC:
|
|
playlist_hist = g_defaults.music_history;
|
|
core_name = "musicplayer";
|
|
core_path = "builtin";
|
|
break;
|
|
case RARCH_CONTENT_IMAGE:
|
|
#ifdef HAVE_IMAGEVIEWER
|
|
playlist_hist = g_defaults.image_history;
|
|
core_name = "imageviewer";
|
|
core_path = "builtin";
|
|
#endif
|
|
break;
|
|
default:
|
|
{
|
|
core_info_t *core_info = NULL;
|
|
/* Set core display name
|
|
* (As far as I can tell, core_info_get_current_core()
|
|
* should always provide a valid pointer here...) */
|
|
core_info_get_current_core(&core_info);
|
|
|
|
/* Set core path */
|
|
core_path = path_get(RARCH_PATH_CORE);
|
|
|
|
if (core_info)
|
|
core_name = core_info->display_name;
|
|
|
|
if (string_is_empty(core_name))
|
|
core_name = info->library_name;
|
|
|
|
if (launched_from_companion_ui)
|
|
{
|
|
/* Database name + checksum are supplied
|
|
* by the companion UI itself */
|
|
if (!string_is_empty(p_content->companion_ui_crc32))
|
|
crc32 = p_content->companion_ui_crc32;
|
|
|
|
if (!string_is_empty(p_content->companion_ui_db_name))
|
|
db_name = p_content->companion_ui_db_name;
|
|
}
|
|
#ifdef HAVE_MENU
|
|
else
|
|
{
|
|
menu_handle_t *menu = menu_driver_get_ptr();
|
|
/* Set database name + checksum */
|
|
if (menu)
|
|
{
|
|
playlist_t *playlist_curr = playlist_get_cached();
|
|
|
|
if (playlist_index_is_valid(playlist_curr, menu->rpl_entry_selection_ptr, tmp, core_path))
|
|
{
|
|
playlist_get_crc32(playlist_curr, menu->rpl_entry_selection_ptr, &crc32);
|
|
playlist_get_db_name(playlist_curr, menu->rpl_entry_selection_ptr, &db_name);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (global && !string_is_empty(global->name.label))
|
|
label = global->name.label;
|
|
|
|
if (
|
|
settings && settings->bools.history_list_enable
|
|
&& playlist_hist)
|
|
{
|
|
char subsystem_name[PATH_MAX_LENGTH];
|
|
struct playlist_entry entry = {0};
|
|
|
|
subsystem_name[0] = '\0';
|
|
|
|
content_get_subsystem_friendly_name(path_get(RARCH_PATH_SUBSYSTEM), subsystem_name, sizeof(subsystem_name));
|
|
|
|
/* The push function reads our entry as const,
|
|
* so these casts are safe */
|
|
entry.path = (char*)tmp;
|
|
entry.label = (char*)label;
|
|
entry.core_path = (char*)core_path;
|
|
entry.core_name = (char*)core_name;
|
|
entry.crc32 = (char*)crc32;
|
|
entry.db_name = (char*)db_name;
|
|
entry.subsystem_ident = (char*)path_get(RARCH_PATH_SUBSYSTEM);
|
|
entry.subsystem_name = (char*)subsystem_name;
|
|
entry.subsystem_roms = (struct string_list*)path_get_subsystem_list();
|
|
|
|
command_playlist_push_write(playlist_hist, &entry);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef HAVE_MENU
|
|
static bool command_event_cmd_exec(
|
|
content_state_t *p_content,
|
|
const char *data,
|
|
content_information_ctx_t *content_ctx,
|
|
bool launched_from_cli,
|
|
char **error_string)
|
|
{
|
|
if (path_get(RARCH_PATH_CONTENT) != data)
|
|
{
|
|
path_clear(RARCH_PATH_CONTENT);
|
|
if (!string_is_empty(data))
|
|
path_set(RARCH_PATH_CONTENT, data);
|
|
}
|
|
|
|
#if defined(HAVE_DYNAMIC)
|
|
{
|
|
content_ctx_info_t content_info;
|
|
|
|
content_info.argc = 0;
|
|
content_info.argv = NULL;
|
|
content_info.args = NULL;
|
|
content_info.environ_get = menu_content_environment_get;
|
|
|
|
/* Loads content into currently selected core. */
|
|
if (!content_load(&content_info, p_content))
|
|
return false;
|
|
task_push_to_history_list(p_content, true, launched_from_cli, false);
|
|
}
|
|
#else
|
|
frontend_driver_set_fork(FRONTEND_FORK_CORE_WITH_ARGS);
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
static bool firmware_update_status(
|
|
content_information_ctx_t *content_ctx)
|
|
{
|
|
char s[PATH_MAX_LENGTH];
|
|
core_info_ctx_firmware_t firmware_info;
|
|
bool set_missing_firmware = false;
|
|
core_info_t *core_info = NULL;
|
|
|
|
core_info_get_current_core(&core_info);
|
|
|
|
if (!core_info)
|
|
return false;
|
|
|
|
s[0] = '\0';
|
|
firmware_info.path = core_info->path;
|
|
|
|
if (!string_is_empty(content_ctx->directory_system))
|
|
firmware_info.directory.system = content_ctx->directory_system;
|
|
else
|
|
{
|
|
strlcpy(s, path_get(RARCH_PATH_CONTENT), sizeof(s));
|
|
path_basedir_wrapper(s);
|
|
firmware_info.directory.system = s;
|
|
}
|
|
|
|
RARCH_LOG("[CONTENT LOAD]: Updating firmware status for: %s on %s\n",
|
|
core_info->path,
|
|
firmware_info.directory.system);
|
|
|
|
rarch_ctl(RARCH_CTL_UNSET_MISSING_BIOS, NULL);
|
|
|
|
core_info_list_update_missing_firmware(&firmware_info,
|
|
&set_missing_firmware);
|
|
|
|
if (set_missing_firmware)
|
|
rarch_ctl(RARCH_CTL_SET_MISSING_BIOS, NULL);
|
|
|
|
if (
|
|
content_ctx->bios_is_missing &&
|
|
content_ctx->check_firmware_before_loading)
|
|
{
|
|
runloop_msg_queue_push(
|
|
msg_hash_to_str(MSG_FIRMWARE),
|
|
100, 500, true, NULL,
|
|
MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
|
|
RARCH_LOG("[CONTENT LOAD]: Load content blocked. Reason: %s\n",
|
|
msg_hash_to_str(MSG_FIRMWARE));
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool task_push_start_dummy_core(content_ctx_info_t *content_info)
|
|
{
|
|
content_information_ctx_t content_ctx;
|
|
content_state_t *p_content = content_state_get_ptr();
|
|
bool ret = true;
|
|
char *error_string = NULL;
|
|
global_t *global = global_get_ptr();
|
|
settings_t *settings = config_get_ptr();
|
|
rarch_system_info_t *sys_info = runloop_get_system_info();
|
|
const char *path_dir_system = settings->paths.directory_system;
|
|
bool check_firmware_before_loading = settings->bools.check_firmware_before_loading;
|
|
|
|
if (!content_info)
|
|
return false;
|
|
|
|
content_ctx.check_firmware_before_loading = check_firmware_before_loading;
|
|
#ifdef HAVE_PATCH
|
|
content_ctx.is_ips_pref = rarch_ctl(RARCH_CTL_IS_IPS_PREF, NULL);
|
|
content_ctx.is_bps_pref = rarch_ctl(RARCH_CTL_IS_BPS_PREF, NULL);
|
|
content_ctx.is_ups_pref = rarch_ctl(RARCH_CTL_IS_UPS_PREF, NULL);
|
|
content_ctx.patch_is_blocked = rarch_ctl(RARCH_CTL_IS_PATCH_BLOCKED, NULL);
|
|
#endif
|
|
content_ctx.bios_is_missing = rarch_ctl(RARCH_CTL_IS_MISSING_BIOS, NULL);
|
|
content_ctx.directory_system = NULL;
|
|
content_ctx.directory_cache = NULL;
|
|
content_ctx.name_ips = NULL;
|
|
content_ctx.name_bps = NULL;
|
|
content_ctx.name_ups = NULL;
|
|
content_ctx.valid_extensions = NULL;
|
|
content_ctx.block_extract = false;
|
|
content_ctx.need_fullpath = false;
|
|
content_ctx.set_supports_no_game_enable = false;
|
|
|
|
content_ctx.subsystem.data = NULL;
|
|
content_ctx.subsystem.size = 0;
|
|
|
|
if (global)
|
|
{
|
|
if (!string_is_empty(global->name.ips))
|
|
content_ctx.name_ips = strdup(global->name.ips);
|
|
if (!string_is_empty(global->name.bps))
|
|
content_ctx.name_bps = strdup(global->name.bps);
|
|
if (!string_is_empty(global->name.ups))
|
|
content_ctx.name_ups = strdup(global->name.ups);
|
|
}
|
|
|
|
if (!string_is_empty(path_dir_system))
|
|
content_ctx.directory_system = strdup(path_dir_system);
|
|
|
|
if (!content_info->environ_get)
|
|
content_info->environ_get = menu_content_environment_get;
|
|
|
|
/* Clear content path */
|
|
path_clear(RARCH_PATH_CONTENT);
|
|
|
|
/* Preliminary stuff that has to be done before we
|
|
* load the actual content. Can differ per mode. */
|
|
sys_info->load_no_content = false;
|
|
rarch_ctl(RARCH_CTL_STATE_FREE, NULL);
|
|
task_queue_deinit();
|
|
retroarch_init_task_queue();
|
|
|
|
/* Loads content into currently selected core. */
|
|
if (!content_load(content_info, p_content))
|
|
{
|
|
if (error_string)
|
|
{
|
|
runloop_msg_queue_push(error_string, 2, 90, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
|
|
RARCH_ERR("[CONTENT LOAD]: %s\n", error_string);
|
|
free(error_string);
|
|
}
|
|
|
|
ret = false;
|
|
}
|
|
else
|
|
task_push_to_history_list(p_content, false, false, false);
|
|
|
|
if (content_ctx.name_ips)
|
|
free(content_ctx.name_ips);
|
|
if (content_ctx.name_bps)
|
|
free(content_ctx.name_bps);
|
|
if (content_ctx.name_ups)
|
|
free(content_ctx.name_ups);
|
|
if (content_ctx.directory_system)
|
|
free(content_ctx.directory_system);
|
|
|
|
return ret;
|
|
}
|
|
|
|
#ifdef HAVE_MENU
|
|
|
|
bool task_push_load_content_from_playlist_from_menu(
|
|
const char *core_path,
|
|
const char *fullpath,
|
|
const char *label,
|
|
content_ctx_info_t *content_info,
|
|
retro_task_callback_t cb,
|
|
void *user_data)
|
|
{
|
|
content_information_ctx_t content_ctx;
|
|
|
|
content_state_t *p_content = content_state_get_ptr();
|
|
bool ret = true;
|
|
char *error_string = NULL;
|
|
global_t *global = global_get_ptr();
|
|
settings_t *settings = config_get_ptr();
|
|
rarch_system_info_t *sys_info = runloop_get_system_info();
|
|
const char *path_dir_system = settings->paths.directory_system;
|
|
#ifndef HAVE_DYNAMIC
|
|
bool force_core_reload = settings->bools.always_reload_core_on_run_content;
|
|
#endif
|
|
|
|
content_ctx.check_firmware_before_loading = settings->bools.check_firmware_before_loading;
|
|
#ifdef HAVE_PATCH
|
|
content_ctx.is_ips_pref = rarch_ctl(RARCH_CTL_IS_IPS_PREF, NULL);
|
|
content_ctx.is_bps_pref = rarch_ctl(RARCH_CTL_IS_BPS_PREF, NULL);
|
|
content_ctx.is_ups_pref = rarch_ctl(RARCH_CTL_IS_UPS_PREF, NULL);
|
|
content_ctx.patch_is_blocked = rarch_ctl(RARCH_CTL_IS_PATCH_BLOCKED, NULL);
|
|
#endif
|
|
content_ctx.bios_is_missing = rarch_ctl(RARCH_CTL_IS_MISSING_BIOS, NULL);
|
|
content_ctx.directory_system = NULL;
|
|
content_ctx.directory_cache = NULL;
|
|
content_ctx.name_ips = NULL;
|
|
content_ctx.name_bps = NULL;
|
|
content_ctx.name_ups = NULL;
|
|
content_ctx.valid_extensions = NULL;
|
|
content_ctx.block_extract = false;
|
|
content_ctx.need_fullpath = false;
|
|
content_ctx.set_supports_no_game_enable = false;
|
|
|
|
content_ctx.subsystem.data = NULL;
|
|
content_ctx.subsystem.size = 0;
|
|
|
|
if (global)
|
|
{
|
|
if (!string_is_empty(global->name.ips))
|
|
content_ctx.name_ips = strdup(global->name.ips);
|
|
if (!string_is_empty(global->name.bps))
|
|
content_ctx.name_bps = strdup(global->name.bps);
|
|
if (!string_is_empty(global->name.ups))
|
|
content_ctx.name_ups = strdup(global->name.ups);
|
|
if (label)
|
|
strlcpy(global->name.label, label, sizeof(global->name.label));
|
|
else
|
|
global->name.label[0] = '\0';
|
|
}
|
|
|
|
if (!string_is_empty(path_dir_system))
|
|
content_ctx.directory_system = strdup(path_dir_system);
|
|
|
|
/* Is content required by this core? */
|
|
if (fullpath)
|
|
sys_info->load_no_content = false;
|
|
else
|
|
sys_info->load_no_content = true;
|
|
|
|
#ifndef HAVE_DYNAMIC
|
|
/* Check whether specified core is already loaded
|
|
* > If so, content can be launched directly with
|
|
* the currently loaded core */
|
|
if (!force_core_reload &&
|
|
rarch_ctl(RARCH_CTL_IS_CORE_LOADED, (void*)core_path))
|
|
{
|
|
if (!content_info->environ_get)
|
|
content_info->environ_get = menu_content_environment_get;
|
|
|
|
/* Register content path */
|
|
path_clear(RARCH_PATH_CONTENT);
|
|
if (!string_is_empty(fullpath))
|
|
path_set(RARCH_PATH_CONTENT, fullpath);
|
|
|
|
/* Load content */
|
|
ret = content_load(content_info, p_content);
|
|
|
|
if (!ret)
|
|
goto end;
|
|
|
|
/* Update content history */
|
|
task_push_to_history_list(p_content, true, false, false);
|
|
|
|
goto end;
|
|
}
|
|
#endif
|
|
|
|
/* Specified core is not loaded
|
|
* > Load it */
|
|
path_set(RARCH_PATH_CORE, core_path);
|
|
#ifdef HAVE_DYNAMIC
|
|
command_event(CMD_EVENT_LOAD_CORE, NULL);
|
|
#endif
|
|
|
|
/* Load content
|
|
* > On targets that do not support dynamic core loading,
|
|
* command_event_cmd_exec() will fork a new instance */
|
|
ret = command_event_cmd_exec(p_content,
|
|
fullpath, &content_ctx, false, &error_string);
|
|
|
|
if (!ret)
|
|
goto end;
|
|
|
|
#ifdef HAVE_COCOATOUCH
|
|
/* This seems to be needed for iOS for some reason
|
|
* to show the quick menu after the menu is shown */
|
|
menu_driver_ctl(RARCH_MENU_CTL_SET_PENDING_QUICK_MENU, NULL);
|
|
#endif
|
|
|
|
#ifndef HAVE_DYNAMIC
|
|
/* No dynamic core loading support: if we reach
|
|
* this point then a new instance has been
|
|
* forked - have to shut down this one */
|
|
rarch_ctl(RARCH_CTL_SET_SHUTDOWN, NULL);
|
|
retroarch_menu_running_finished(true);
|
|
#endif
|
|
|
|
end:
|
|
/* Handle load content failure */
|
|
if (!ret)
|
|
{
|
|
if (error_string)
|
|
{
|
|
runloop_msg_queue_push(error_string, 2, 90, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
|
|
RARCH_ERR("[CONTENT LOAD]: %s\n", error_string);
|
|
free(error_string);
|
|
}
|
|
|
|
retroarch_menu_running();
|
|
}
|
|
|
|
if (content_ctx.name_ips)
|
|
free(content_ctx.name_ips);
|
|
if (content_ctx.name_bps)
|
|
free(content_ctx.name_bps);
|
|
if (content_ctx.name_ups)
|
|
free(content_ctx.name_ups);
|
|
if (content_ctx.directory_system)
|
|
free(content_ctx.directory_system);
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
bool task_push_start_current_core(content_ctx_info_t *content_info)
|
|
{
|
|
content_information_ctx_t content_ctx;
|
|
|
|
content_state_t *p_content = content_state_get_ptr();
|
|
bool ret = true;
|
|
char *error_string = NULL;
|
|
global_t *global = global_get_ptr();
|
|
settings_t *settings = config_get_ptr();
|
|
const char *path_dir_system = settings->paths.directory_system;
|
|
bool check_firmware_before_loading = settings->bools.check_firmware_before_loading;
|
|
|
|
if (!content_info)
|
|
return false;
|
|
|
|
content_ctx.check_firmware_before_loading = check_firmware_before_loading;
|
|
#ifdef HAVE_PATCH
|
|
content_ctx.is_ips_pref = rarch_ctl(RARCH_CTL_IS_IPS_PREF, NULL);
|
|
content_ctx.is_bps_pref = rarch_ctl(RARCH_CTL_IS_BPS_PREF, NULL);
|
|
content_ctx.is_ups_pref = rarch_ctl(RARCH_CTL_IS_UPS_PREF, NULL);
|
|
content_ctx.patch_is_blocked = rarch_ctl(RARCH_CTL_IS_PATCH_BLOCKED, NULL);
|
|
#endif
|
|
content_ctx.bios_is_missing = rarch_ctl(RARCH_CTL_IS_MISSING_BIOS, NULL);
|
|
content_ctx.directory_system = NULL;
|
|
content_ctx.directory_cache = NULL;
|
|
content_ctx.name_ips = NULL;
|
|
content_ctx.name_bps = NULL;
|
|
content_ctx.name_ups = NULL;
|
|
content_ctx.valid_extensions = NULL;
|
|
content_ctx.block_extract = false;
|
|
content_ctx.need_fullpath = false;
|
|
content_ctx.set_supports_no_game_enable = false;
|
|
|
|
content_ctx.subsystem.data = NULL;
|
|
content_ctx.subsystem.size = 0;
|
|
|
|
if (global)
|
|
{
|
|
if (!string_is_empty(global->name.ips))
|
|
content_ctx.name_ips = strdup(global->name.ips);
|
|
if (!string_is_empty(global->name.bps))
|
|
content_ctx.name_bps = strdup(global->name.bps);
|
|
if (!string_is_empty(global->name.ups))
|
|
content_ctx.name_ups = strdup(global->name.ups);
|
|
}
|
|
|
|
if (!string_is_empty(path_dir_system))
|
|
content_ctx.directory_system = strdup(path_dir_system);
|
|
|
|
if (!content_info->environ_get)
|
|
content_info->environ_get = menu_content_environment_get;
|
|
|
|
/* Clear content path */
|
|
path_clear(RARCH_PATH_CONTENT);
|
|
|
|
/* Preliminary stuff that has to be done before we
|
|
* load the actual content. Can differ per mode. */
|
|
retroarch_set_current_core_type(CORE_TYPE_PLAIN, true);
|
|
|
|
/* Load content */
|
|
if (firmware_update_status(&content_ctx))
|
|
goto end;
|
|
|
|
/* Loads content into currently selected core. */
|
|
if (!content_load(content_info, p_content))
|
|
{
|
|
if (error_string)
|
|
{
|
|
runloop_msg_queue_push(error_string, 2, 90, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
|
|
RARCH_ERR("[CONTENT LOAD]: %s\n", error_string);
|
|
free(error_string);
|
|
}
|
|
|
|
retroarch_menu_running();
|
|
|
|
ret = false;
|
|
goto end;
|
|
}
|
|
else
|
|
task_push_to_history_list(p_content, true, false, false);
|
|
|
|
#ifdef HAVE_MENU
|
|
/* Push quick menu onto menu stack */
|
|
menu_driver_ctl(RARCH_MENU_CTL_SET_PENDING_QUICK_MENU, NULL);
|
|
#endif
|
|
|
|
end:
|
|
if (content_ctx.name_ips)
|
|
free(content_ctx.name_ips);
|
|
if (content_ctx.name_bps)
|
|
free(content_ctx.name_bps);
|
|
if (content_ctx.name_ups)
|
|
free(content_ctx.name_ups);
|
|
if (content_ctx.directory_system)
|
|
free(content_ctx.directory_system);
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool task_push_load_new_core(
|
|
const char *core_path,
|
|
const char *fullpath,
|
|
content_ctx_info_t *content_info,
|
|
enum rarch_core_type type,
|
|
retro_task_callback_t cb,
|
|
void *user_data)
|
|
{
|
|
path_set(RARCH_PATH_CORE, core_path);
|
|
|
|
/* Load core */
|
|
command_event(CMD_EVENT_LOAD_CORE, NULL);
|
|
|
|
#ifndef HAVE_DYNAMIC
|
|
/* Fork core? */
|
|
if (!frontend_driver_set_fork(FRONTEND_FORK_CORE))
|
|
return false;
|
|
#endif
|
|
|
|
/* Preliminary stuff that has to be done before we
|
|
* load the actual content. Can differ per mode. */
|
|
retroarch_set_current_core_type(type, true);
|
|
|
|
return true;
|
|
}
|
|
|
|
#ifdef HAVE_MENU
|
|
bool task_push_load_content_with_new_core_from_menu(
|
|
const char *core_path,
|
|
const char *fullpath,
|
|
content_ctx_info_t *content_info,
|
|
enum rarch_core_type type,
|
|
retro_task_callback_t cb,
|
|
void *user_data)
|
|
{
|
|
content_information_ctx_t content_ctx;
|
|
|
|
content_state_t *p_content = content_state_get_ptr();
|
|
bool ret = true;
|
|
char *error_string = NULL;
|
|
global_t *global = global_get_ptr();
|
|
settings_t *settings = config_get_ptr();
|
|
bool check_firmware_before_loading = settings->bools.check_firmware_before_loading;
|
|
const char *path_dir_system = settings->paths.directory_system;
|
|
#ifndef HAVE_DYNAMIC
|
|
bool force_core_reload = settings->bools.always_reload_core_on_run_content;
|
|
|
|
/* Check whether specified core is already loaded
|
|
* > If so, we can skip loading the core and
|
|
* just load the content directly */
|
|
if (!force_core_reload &&
|
|
(type == CORE_TYPE_PLAIN) &&
|
|
rarch_ctl(RARCH_CTL_IS_CORE_LOADED, (void*)core_path))
|
|
return task_push_load_content_with_core_from_menu(
|
|
fullpath, content_info,
|
|
type, cb, user_data);
|
|
#endif
|
|
|
|
content_ctx.check_firmware_before_loading = check_firmware_before_loading;
|
|
#ifdef HAVE_PATCH
|
|
content_ctx.is_ips_pref = rarch_ctl(RARCH_CTL_IS_IPS_PREF, NULL);
|
|
content_ctx.is_bps_pref = rarch_ctl(RARCH_CTL_IS_BPS_PREF, NULL);
|
|
content_ctx.is_ups_pref = rarch_ctl(RARCH_CTL_IS_UPS_PREF, NULL);
|
|
content_ctx.patch_is_blocked = rarch_ctl(RARCH_CTL_IS_PATCH_BLOCKED, NULL);
|
|
#endif
|
|
content_ctx.bios_is_missing = rarch_ctl(RARCH_CTL_IS_MISSING_BIOS, NULL);
|
|
content_ctx.directory_system = NULL;
|
|
content_ctx.directory_cache = NULL;
|
|
content_ctx.name_ips = NULL;
|
|
content_ctx.name_bps = NULL;
|
|
content_ctx.name_ups = NULL;
|
|
content_ctx.valid_extensions = NULL;
|
|
content_ctx.block_extract = false;
|
|
content_ctx.need_fullpath = false;
|
|
content_ctx.set_supports_no_game_enable = false;
|
|
|
|
content_ctx.subsystem.data = NULL;
|
|
content_ctx.subsystem.size = 0;
|
|
|
|
if (global)
|
|
{
|
|
if (!string_is_empty(global->name.ips))
|
|
content_ctx.name_ips = strdup(global->name.ips);
|
|
if (!string_is_empty(global->name.bps))
|
|
content_ctx.name_bps = strdup(global->name.bps);
|
|
if (!string_is_empty(global->name.ups))
|
|
content_ctx.name_ups = strdup(global->name.ups);
|
|
|
|
global->name.label[0] = '\0';
|
|
}
|
|
|
|
if (!string_is_empty(path_dir_system))
|
|
content_ctx.directory_system = strdup(path_dir_system);
|
|
|
|
path_set(RARCH_PATH_CONTENT, fullpath);
|
|
path_set(RARCH_PATH_CORE, core_path);
|
|
|
|
#ifdef HAVE_DYNAMIC
|
|
/* Load core */
|
|
command_event(CMD_EVENT_LOAD_CORE, NULL);
|
|
|
|
/* Load content */
|
|
if (!content_info->environ_get)
|
|
content_info->environ_get = menu_content_environment_get;
|
|
|
|
if (firmware_update_status(&content_ctx))
|
|
goto end;
|
|
|
|
/* Loads content into currently selected core. */
|
|
if (!content_load(content_info, p_content))
|
|
{
|
|
if (error_string)
|
|
{
|
|
runloop_msg_queue_push(error_string, 2, 90, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
|
|
RARCH_ERR("[CONTENT LOAD]: %s\n", error_string);
|
|
free(error_string);
|
|
}
|
|
|
|
retroarch_menu_running();
|
|
|
|
ret = false;
|
|
goto end;
|
|
}
|
|
else
|
|
task_push_to_history_list(p_content, true, false, false);
|
|
#else
|
|
command_event_cmd_exec(p_content,
|
|
path_get(RARCH_PATH_CONTENT), &content_ctx,
|
|
false, &error_string);
|
|
command_event(CMD_EVENT_QUIT, NULL);
|
|
#endif
|
|
|
|
/* Push quick menu onto menu stack */
|
|
if (type != CORE_TYPE_DUMMY)
|
|
menu_driver_ctl(RARCH_MENU_CTL_SET_PENDING_QUICK_MENU, NULL);
|
|
|
|
#ifdef HAVE_DYNAMIC
|
|
end:
|
|
#endif
|
|
if (content_ctx.name_ips)
|
|
free(content_ctx.name_ips);
|
|
if (content_ctx.name_bps)
|
|
free(content_ctx.name_bps);
|
|
if (content_ctx.name_ups)
|
|
free(content_ctx.name_ups);
|
|
if (content_ctx.directory_system)
|
|
free(content_ctx.directory_system);
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
static bool task_load_content_internal(
|
|
content_ctx_info_t *content_info,
|
|
bool loading_from_menu,
|
|
bool loading_from_cli,
|
|
bool loading_from_companion_ui)
|
|
{
|
|
content_information_ctx_t content_ctx;
|
|
|
|
content_state_t *p_content = content_state_get_ptr();
|
|
bool ret = false;
|
|
char *error_string = NULL;
|
|
global_t *global = global_get_ptr();
|
|
rarch_system_info_t *sys_info = runloop_get_system_info();
|
|
settings_t *settings = config_get_ptr();
|
|
bool check_firmware_before_loading = settings->bools.check_firmware_before_loading;
|
|
bool set_supports_no_game_enable = settings->bools.set_supports_no_game_enable;
|
|
const char *path_dir_system = settings->paths.directory_system;
|
|
const char *path_dir_cache = settings->paths.directory_cache;
|
|
|
|
content_ctx.check_firmware_before_loading = check_firmware_before_loading;
|
|
#ifdef HAVE_PATCH
|
|
content_ctx.is_ips_pref = rarch_ctl(RARCH_CTL_IS_IPS_PREF, NULL);
|
|
content_ctx.is_bps_pref = rarch_ctl(RARCH_CTL_IS_BPS_PREF, NULL);
|
|
content_ctx.is_ups_pref = rarch_ctl(RARCH_CTL_IS_UPS_PREF, NULL);
|
|
content_ctx.patch_is_blocked = rarch_ctl(RARCH_CTL_IS_PATCH_BLOCKED, NULL);
|
|
#endif
|
|
content_ctx.bios_is_missing = rarch_ctl(RARCH_CTL_IS_MISSING_BIOS, NULL);
|
|
content_ctx.directory_system = NULL;
|
|
content_ctx.directory_cache = NULL;
|
|
content_ctx.name_ips = NULL;
|
|
content_ctx.name_bps = NULL;
|
|
content_ctx.name_ups = NULL;
|
|
content_ctx.valid_extensions = NULL;
|
|
content_ctx.block_extract = false;
|
|
content_ctx.need_fullpath = false;
|
|
content_ctx.set_supports_no_game_enable = false;
|
|
|
|
content_ctx.subsystem.data = NULL;
|
|
content_ctx.subsystem.size = 0;
|
|
|
|
if (sys_info)
|
|
{
|
|
struct retro_system_info *system = runloop_get_libretro_system_info();
|
|
|
|
content_ctx.set_supports_no_game_enable = set_supports_no_game_enable;
|
|
|
|
if (!string_is_empty(path_dir_cache))
|
|
content_ctx.directory_cache = strdup(path_dir_cache);
|
|
if (!string_is_empty(system->valid_extensions))
|
|
content_ctx.valid_extensions = strdup(system->valid_extensions);
|
|
|
|
content_ctx.block_extract = system->block_extract;
|
|
content_ctx.need_fullpath = system->need_fullpath;
|
|
|
|
content_ctx.subsystem.data = sys_info->subsystem.data;
|
|
content_ctx.subsystem.size = sys_info->subsystem.size;
|
|
}
|
|
|
|
if (global)
|
|
{
|
|
if (!string_is_empty(global->name.ips))
|
|
content_ctx.name_ips = strdup(global->name.ips);
|
|
if (!string_is_empty(global->name.bps))
|
|
content_ctx.name_bps = strdup(global->name.bps);
|
|
if (!string_is_empty(global->name.ups))
|
|
content_ctx.name_ups = strdup(global->name.ups);
|
|
}
|
|
|
|
if (!string_is_empty(path_dir_system))
|
|
content_ctx.directory_system = strdup(path_dir_system);
|
|
|
|
if (!content_info->environ_get)
|
|
content_info->environ_get = menu_content_environment_get;
|
|
|
|
if (firmware_update_status(&content_ctx))
|
|
goto end;
|
|
|
|
#ifdef HAVE_DISCORD
|
|
if (discord_is_inited)
|
|
{
|
|
discord_userdata_t userdata;
|
|
userdata.status = DISCORD_PRESENCE_NETPLAY_NETPLAY_STOPPED;
|
|
command_event(CMD_EVENT_DISCORD_UPDATE, &userdata);
|
|
userdata.status = DISCORD_PRESENCE_MENU;
|
|
command_event(CMD_EVENT_DISCORD_UPDATE, &userdata);
|
|
}
|
|
#endif
|
|
|
|
/* Loads content into currently selected core. */
|
|
ret = content_load(content_info, p_content);
|
|
|
|
if (ret)
|
|
task_push_to_history_list(p_content,
|
|
true, loading_from_cli, loading_from_companion_ui);
|
|
|
|
end:
|
|
if (content_ctx.name_ips)
|
|
free(content_ctx.name_ips);
|
|
if (content_ctx.name_bps)
|
|
free(content_ctx.name_bps);
|
|
if (content_ctx.name_ups)
|
|
free(content_ctx.name_ups);
|
|
if (content_ctx.directory_system)
|
|
free(content_ctx.directory_system);
|
|
if (content_ctx.directory_cache)
|
|
free(content_ctx.directory_cache);
|
|
if (content_ctx.valid_extensions)
|
|
free(content_ctx.valid_extensions);
|
|
|
|
if (!ret)
|
|
{
|
|
if (error_string)
|
|
{
|
|
runloop_msg_queue_push(error_string, 2, 90, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
|
|
RARCH_ERR("[CONTENT LOAD]: %s\n", error_string);
|
|
free(error_string);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool task_push_load_content_with_new_core_from_companion_ui(
|
|
const char *core_path,
|
|
const char *fullpath,
|
|
const char *label,
|
|
const char *db_name,
|
|
const char *crc32,
|
|
content_ctx_info_t *content_info,
|
|
retro_task_callback_t cb,
|
|
void *user_data)
|
|
{
|
|
global_t *global = global_get_ptr();
|
|
content_state_t *p_content = content_state_get_ptr();
|
|
|
|
path_set(RARCH_PATH_CONTENT, fullpath);
|
|
path_set(RARCH_PATH_CORE, core_path);
|
|
|
|
p_content->companion_ui_db_name[0] = '\0';
|
|
p_content->companion_ui_crc32[0] = '\0';
|
|
|
|
if (!string_is_empty(db_name))
|
|
strlcpy(p_content->companion_ui_db_name,
|
|
db_name, sizeof(p_content->companion_ui_db_name));
|
|
|
|
if (!string_is_empty(crc32))
|
|
strlcpy(p_content->companion_ui_crc32,
|
|
crc32, sizeof(p_content->companion_ui_crc32));
|
|
|
|
#ifdef HAVE_DYNAMIC
|
|
command_event(CMD_EVENT_LOAD_CORE, NULL);
|
|
#endif
|
|
|
|
global->launched_from_cli = false;
|
|
|
|
if (global)
|
|
{
|
|
if (label)
|
|
strlcpy(global->name.label, label, sizeof(global->name.label));
|
|
else
|
|
global->name.label[0] = '\0';
|
|
}
|
|
|
|
/* Load content */
|
|
if (!task_load_content_internal(content_info, true, false, true))
|
|
return false;
|
|
|
|
#ifdef HAVE_MENU
|
|
/* Push quick menu onto menu stack */
|
|
menu_driver_ctl(RARCH_MENU_CTL_SET_PENDING_QUICK_MENU, NULL);
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
bool task_push_load_content_from_cli(
|
|
const char *core_path,
|
|
const char *fullpath,
|
|
content_ctx_info_t *content_info,
|
|
enum rarch_core_type type,
|
|
retro_task_callback_t cb,
|
|
void *user_data)
|
|
{
|
|
return task_load_content_internal(content_info, true, true, false);
|
|
}
|
|
|
|
bool task_push_start_builtin_core(
|
|
content_ctx_info_t *content_info,
|
|
enum rarch_core_type type,
|
|
retro_task_callback_t cb,
|
|
void *user_data)
|
|
{
|
|
/* Clear content path */
|
|
path_clear(RARCH_PATH_CONTENT);
|
|
|
|
/* Preliminary stuff that has to be done before we
|
|
* load the actual content. Can differ per mode. */
|
|
retroarch_set_current_core_type(type, true);
|
|
|
|
/* Load content */
|
|
if (!task_load_content_internal(content_info, true, false, false))
|
|
{
|
|
retroarch_menu_running();
|
|
return false;
|
|
}
|
|
|
|
/* Push quick menu onto menu stack */
|
|
#ifdef HAVE_MENU
|
|
menu_driver_ctl(RARCH_MENU_CTL_SET_PENDING_QUICK_MENU, NULL);
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
bool task_push_load_content_with_core_from_menu(
|
|
const char *fullpath,
|
|
content_ctx_info_t *content_info,
|
|
enum rarch_core_type type,
|
|
retro_task_callback_t cb,
|
|
void *user_data)
|
|
{
|
|
path_set(RARCH_PATH_CONTENT, fullpath);
|
|
|
|
/* Load content */
|
|
if (!task_load_content_internal(content_info, true, false, false))
|
|
{
|
|
retroarch_menu_running();
|
|
return false;
|
|
}
|
|
|
|
#ifdef HAVE_MENU
|
|
/* Push quick menu onto menu stack */
|
|
if (type != CORE_TYPE_DUMMY)
|
|
menu_driver_ctl(RARCH_MENU_CTL_SET_PENDING_QUICK_MENU, NULL);
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
bool task_push_load_content_with_current_core_from_companion_ui(
|
|
const char *fullpath,
|
|
content_ctx_info_t *content_info,
|
|
enum rarch_core_type type,
|
|
retro_task_callback_t cb,
|
|
void *user_data)
|
|
{
|
|
content_state_t *p_content = content_state_get_ptr();
|
|
|
|
/* TODO/FIXME: Enable setting of these values
|
|
* via function arguments */
|
|
p_content->companion_ui_db_name[0] = '\0';
|
|
p_content->companion_ui_crc32[0] = '\0';
|
|
|
|
/* Load content
|
|
* > TODO/FIXME: Set loading_from_companion_ui 'false' for
|
|
* now, until someone can implement the required higher
|
|
* level functionality in 'win32_common.c' and 'ui_cocoa.m' */
|
|
return task_push_load_content_with_core_from_menu(fullpath,
|
|
content_info, type, cb, user_data);
|
|
}
|
|
|
|
|
|
bool task_push_load_subsystem_with_core_from_menu(
|
|
const char *fullpath,
|
|
content_ctx_info_t *content_info,
|
|
enum rarch_core_type type,
|
|
retro_task_callback_t cb,
|
|
void *user_data)
|
|
{
|
|
content_state_t *p_content = content_state_get_ptr();
|
|
|
|
p_content->pending_subsystem_init = true;
|
|
|
|
/* Load content */
|
|
if (!task_load_content_internal(content_info, true, false, false))
|
|
{
|
|
retroarch_menu_running();
|
|
return false;
|
|
}
|
|
|
|
#ifdef HAVE_MENU
|
|
/* Push quick menu onto menu stack */
|
|
if (type != CORE_TYPE_DUMMY)
|
|
menu_driver_ctl(RARCH_MENU_CTL_SET_PENDING_QUICK_MENU, NULL);
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
void content_get_status(
|
|
bool *contentless,
|
|
bool *is_inited)
|
|
{
|
|
content_state_t *p_content = content_state_get_ptr();
|
|
|
|
*contentless = p_content->core_does_not_need_content;
|
|
*is_inited = p_content->is_inited;
|
|
}
|
|
|
|
/* Clears the pending subsystem rom buffer*/
|
|
void content_clear_subsystem(void)
|
|
{
|
|
unsigned i;
|
|
content_state_t *p_content = content_state_get_ptr();
|
|
|
|
p_content->pending_subsystem_rom_id = 0;
|
|
p_content->pending_subsystem_init = false;
|
|
|
|
for (i = 0; i < RARCH_MAX_SUBSYSTEM_ROMS; i++)
|
|
{
|
|
if (p_content->pending_subsystem_roms[i])
|
|
{
|
|
free(p_content->pending_subsystem_roms[i]);
|
|
p_content->pending_subsystem_roms[i] = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Set the current subsystem*/
|
|
void content_set_subsystem(unsigned idx)
|
|
{
|
|
const struct retro_subsystem_info *subsystem;
|
|
rarch_system_info_t *system = runloop_get_system_info();
|
|
content_state_t *p_content = content_state_get_ptr();
|
|
|
|
/* Core fully loaded, use the subsystem data */
|
|
if (system->subsystem.data)
|
|
subsystem = system->subsystem.data + idx;
|
|
/* Core not loaded completely, use the data we peeked on load core */
|
|
else
|
|
subsystem = subsystem_data + idx;
|
|
|
|
p_content->pending_subsystem_id = idx;
|
|
|
|
if (subsystem && subsystem_current_count > 0)
|
|
{
|
|
strlcpy(p_content->pending_subsystem_ident,
|
|
subsystem->ident, sizeof(p_content->pending_subsystem_ident));
|
|
|
|
p_content->pending_subsystem_rom_num = subsystem->num_roms;
|
|
}
|
|
|
|
RARCH_LOG("[Subsystem]: Setting current subsystem to: %d(%s) Content amount: %d\n",
|
|
p_content->pending_subsystem_id,
|
|
p_content->pending_subsystem_ident,
|
|
p_content->pending_subsystem_rom_num);
|
|
}
|
|
|
|
/* Sets the subsystem by name */
|
|
bool content_set_subsystem_by_name(const char* subsystem_name)
|
|
{
|
|
rarch_system_info_t *system = runloop_get_system_info();
|
|
const struct retro_subsystem_info *subsystem;
|
|
unsigned i = 0;
|
|
|
|
/* Core fully loaded, use the subsystem data */
|
|
if (system->subsystem.data)
|
|
subsystem = system->subsystem.data;
|
|
/* Core not loaded completely, use the data we peeked on load core */
|
|
else
|
|
subsystem = subsystem_data;
|
|
|
|
for (i = 0; i < subsystem_current_count; i++, subsystem++)
|
|
{
|
|
if (string_is_equal(subsystem_name, subsystem->ident))
|
|
{
|
|
content_set_subsystem(i);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void content_get_subsystem_friendly_name(const char* subsystem_name, char* subsystem_friendly_name, size_t len)
|
|
{
|
|
rarch_system_info_t *system = runloop_get_system_info();
|
|
const struct retro_subsystem_info *subsystem;
|
|
unsigned i = 0;
|
|
|
|
/* Core fully loaded, use the subsystem data */
|
|
if (system->subsystem.data)
|
|
subsystem = system->subsystem.data;
|
|
/* Core not loaded completely, use the data we peeked on load core */
|
|
else
|
|
subsystem = subsystem_data;
|
|
|
|
for (i = 0; i < subsystem_current_count; i++, subsystem++)
|
|
{
|
|
if (string_is_equal(subsystem_name, subsystem->ident))
|
|
{
|
|
strlcpy(subsystem_friendly_name, subsystem->desc, len);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/* Add a rom to the subsystem ROM buffer */
|
|
void content_add_subsystem(const char* path)
|
|
{
|
|
content_state_t *p_content = content_state_get_ptr();
|
|
size_t pending_size = PATH_MAX_LENGTH * sizeof(char);
|
|
p_content->pending_subsystem_roms[p_content->pending_subsystem_rom_id] = (char*)malloc(pending_size);
|
|
|
|
strlcpy(p_content->pending_subsystem_roms[
|
|
p_content->pending_subsystem_rom_id],
|
|
path, pending_size);
|
|
RARCH_LOG("[Subsystem]: Subsystem id: %d Subsystem ident:"
|
|
" %s Content ID: %d, Content Path: %s\n",
|
|
p_content->pending_subsystem_id,
|
|
p_content->pending_subsystem_ident,
|
|
p_content->pending_subsystem_rom_id,
|
|
p_content->pending_subsystem_roms[
|
|
p_content->pending_subsystem_rom_id]);
|
|
p_content->pending_subsystem_rom_id++;
|
|
}
|
|
|
|
void content_set_does_not_need_content(void)
|
|
{
|
|
content_state_t *p_content = content_state_get_ptr();
|
|
p_content->core_does_not_need_content = true;
|
|
}
|
|
|
|
void content_unset_does_not_need_content(void)
|
|
{
|
|
content_state_t *p_content = content_state_get_ptr();
|
|
p_content->core_does_not_need_content = false;
|
|
}
|
|
|
|
uint32_t content_get_crc(void)
|
|
{
|
|
content_state_t *p_content = content_state_get_ptr();
|
|
if (p_content->pending_rom_crc)
|
|
{
|
|
p_content->pending_rom_crc = false;
|
|
p_content->rom_crc = file_crc32(0,
|
|
(const char*)p_content->pending_rom_crc_path);
|
|
RARCH_LOG("[CONTENT LOAD]: CRC32: 0x%x .\n", (unsigned)p_content->rom_crc);
|
|
}
|
|
return p_content->rom_crc;
|
|
}
|
|
|
|
char* content_get_subsystem_rom(unsigned index)
|
|
{
|
|
content_state_t *p_content = content_state_get_ptr();
|
|
return p_content->pending_subsystem_roms[index];
|
|
}
|
|
|
|
bool content_is_inited(void)
|
|
{
|
|
content_state_t *p_content = content_state_get_ptr();
|
|
return p_content->is_inited;
|
|
}
|
|
|
|
void content_deinit(void)
|
|
{
|
|
unsigned i;
|
|
content_state_t *p_content = content_state_get_ptr();
|
|
|
|
if (p_content->temporary_content)
|
|
{
|
|
for (i = 0; i < p_content->temporary_content->size; i++)
|
|
{
|
|
const char *path = p_content->temporary_content->elems[i].data;
|
|
|
|
RARCH_LOG("[CONTENT LOAD]: %s: %s.\n",
|
|
msg_hash_to_str(MSG_REMOVING_TEMPORARY_CONTENT_FILE), path);
|
|
if (filestream_delete(path) != 0)
|
|
RARCH_ERR("[CONTENT LOAD]: %s: %s.\n",
|
|
msg_hash_to_str(MSG_FAILED_TO_REMOVE_TEMPORARY_FILE),
|
|
path);
|
|
}
|
|
string_list_free(p_content->temporary_content);
|
|
}
|
|
|
|
p_content->temporary_content = NULL;
|
|
p_content->rom_crc = 0;
|
|
p_content->is_inited = false;
|
|
p_content->core_does_not_need_content = false;
|
|
p_content->pending_rom_crc = false;
|
|
}
|
|
|
|
/* Set environment variables before a subsystem load */
|
|
void content_set_subsystem_info(void)
|
|
{
|
|
content_state_t *p_content = content_state_get_ptr();
|
|
if (!p_content->pending_subsystem_init)
|
|
return;
|
|
|
|
path_set(RARCH_PATH_SUBSYSTEM, p_content->pending_subsystem_ident);
|
|
path_set_special(p_content->pending_subsystem_roms,
|
|
p_content->pending_subsystem_rom_num);
|
|
}
|
|
|
|
/* Initializes and loads a content file for the currently
|
|
* selected libretro core. */
|
|
bool content_init(void)
|
|
{
|
|
struct string_list content;
|
|
content_information_ctx_t content_ctx;
|
|
content_state_t *p_content = content_state_get_ptr();
|
|
|
|
bool ret = true;
|
|
char *error_string = NULL;
|
|
global_t *global = global_get_ptr();
|
|
rarch_system_info_t *sys_info = runloop_get_system_info();
|
|
settings_t *settings = config_get_ptr();
|
|
bool check_firmware_before_loading = settings->bools.check_firmware_before_loading;
|
|
bool set_supports_no_game_enable = settings->bools.set_supports_no_game_enable;
|
|
const char *path_dir_system = settings->paths.directory_system;
|
|
const char *path_dir_cache = settings->paths.directory_cache;
|
|
|
|
p_content->temporary_content = string_list_new();
|
|
|
|
content_ctx.check_firmware_before_loading = check_firmware_before_loading;
|
|
#ifdef HAVE_PATCH
|
|
content_ctx.is_ips_pref = rarch_ctl(RARCH_CTL_IS_IPS_PREF, NULL);
|
|
content_ctx.is_bps_pref = rarch_ctl(RARCH_CTL_IS_BPS_PREF, NULL);
|
|
content_ctx.is_ups_pref = rarch_ctl(RARCH_CTL_IS_UPS_PREF, NULL);
|
|
content_ctx.patch_is_blocked = rarch_ctl(RARCH_CTL_IS_PATCH_BLOCKED, NULL);
|
|
#endif
|
|
content_ctx.temporary_content = p_content->temporary_content;
|
|
content_ctx.directory_system = NULL;
|
|
content_ctx.directory_cache = NULL;
|
|
content_ctx.name_ips = NULL;
|
|
content_ctx.name_bps = NULL;
|
|
content_ctx.name_ups = NULL;
|
|
content_ctx.valid_extensions = NULL;
|
|
content_ctx.block_extract = false;
|
|
content_ctx.need_fullpath = false;
|
|
content_ctx.set_supports_no_game_enable = false;
|
|
|
|
content_ctx.subsystem.data = NULL;
|
|
content_ctx.subsystem.size = 0;
|
|
|
|
if (global)
|
|
{
|
|
if (!string_is_empty(global->name.ips))
|
|
content_ctx.name_ips = strdup(global->name.ips);
|
|
if (!string_is_empty(global->name.bps))
|
|
content_ctx.name_bps = strdup(global->name.bps);
|
|
if (!string_is_empty(global->name.ups))
|
|
content_ctx.name_ups = strdup(global->name.ups);
|
|
}
|
|
|
|
if (sys_info)
|
|
{
|
|
struct retro_system_info *system = runloop_get_libretro_system_info();
|
|
|
|
content_ctx.set_supports_no_game_enable = set_supports_no_game_enable;
|
|
|
|
if (!string_is_empty(path_dir_system))
|
|
content_ctx.directory_system = strdup(path_dir_system);
|
|
if (!string_is_empty(path_dir_cache))
|
|
content_ctx.directory_cache = strdup(path_dir_cache);
|
|
if (!string_is_empty(system->valid_extensions))
|
|
content_ctx.valid_extensions = strdup(system->valid_extensions);
|
|
|
|
content_ctx.block_extract = system->block_extract;
|
|
content_ctx.need_fullpath = system->need_fullpath;
|
|
|
|
content_ctx.subsystem.data = sys_info->subsystem.data;
|
|
content_ctx.subsystem.size = sys_info->subsystem.size;
|
|
}
|
|
|
|
p_content->is_inited = true;
|
|
|
|
if (string_list_initialize(&content))
|
|
{
|
|
if ( !p_content->temporary_content
|
|
|| !content_file_init(&content_ctx, p_content,
|
|
&content, &error_string))
|
|
{
|
|
content_deinit();
|
|
|
|
ret = false;
|
|
}
|
|
string_list_deinitialize(&content);
|
|
}
|
|
|
|
if (content_ctx.name_ips)
|
|
free(content_ctx.name_ips);
|
|
if (content_ctx.name_bps)
|
|
free(content_ctx.name_bps);
|
|
if (content_ctx.name_ups)
|
|
free(content_ctx.name_ups);
|
|
if (content_ctx.directory_system)
|
|
free(content_ctx.directory_system);
|
|
if (content_ctx.directory_cache)
|
|
free(content_ctx.directory_cache);
|
|
if (content_ctx.valid_extensions)
|
|
free(content_ctx.valid_extensions);
|
|
|
|
if (error_string)
|
|
{
|
|
if (ret)
|
|
{
|
|
RARCH_LOG("[CONTENT LOAD]: %s\n", error_string);
|
|
}
|
|
else
|
|
{
|
|
RARCH_ERR("[CONTENT LOAD]: %s\n", error_string);
|
|
}
|
|
/* Do not flush the message queue here
|
|
* > This allows any core-generated error messages
|
|
* to propagate through to the frontend */
|
|
runloop_msg_queue_push(error_string, 2, ret ? 1 : 180, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
|
|
free(error_string);
|
|
}
|
|
|
|
return ret;
|
|
}
|