diff --git a/Makefile.common b/Makefile.common
index 9a75be8c24..b203c02371 100644
--- a/Makefile.common
+++ b/Makefile.common
@@ -129,7 +129,6 @@ OBJ += frontend/frontend.o \
tasks/task_content.o \
tasks/task_file_transfer.o \
tasks/task_image.o \
- content.o \
libretro-common/encodings/encoding_utf.o \
libretro-common/lists/file_list.o \
libretro-common/lists/dir_list.o \
diff --git a/content.c b/content.c
deleted file mode 100644
index 4d185b2ad3..0000000000
--- a/content.c
+++ /dev/null
@@ -1,1766 +0,0 @@
-/* RetroArch - A frontend for libretro.
- * Copyright (C) 2010-2014 - Hans-Kristian Arntzen
- * Copyright (C) 2011-2016 - Daniel De Matteis
- *
- * 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 .
- */
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#ifdef _WIN32
-#ifdef _XBOX
-#include
-#define setmode _setmode
-#define INVALID_FILE_ATTRIBUTES -1
-#else
-#include
-#include
-#include
-#endif
-#endif
-
-#include
-
-#include
-#include
-#include
-#include
-#ifdef HAVE_COMPRESSION
-#include
-#endif
-#include
-
-#include
-#include
-#include
-#include
-
-#include
-#include
-
-#include "defaults.h"
-#include "msg_hash.h"
-#include "content.h"
-#include "general.h"
-#include "dynamic.h"
-#include "movie.h"
-#include "patch.h"
-#include "system.h"
-#include "retroarch.h"
-#include "command.h"
-#include "file_path_special.h"
-#include "core.h"
-#include "verbosity.h"
-
-#ifdef HAVE_7ZIP
-#include "deps/7zip/7z.h"
-#include "deps/7zip/7zAlloc.h"
-#include "deps/7zip/7zCrc.h"
-#include "deps/7zip/7zFile.h"
-#endif
-
-#ifdef HAVE_MENU
-#include "menu/menu_driver.h"
-#endif
-
-#ifdef HAVE_CHEEVOS
-#include "cheevos.h"
-#endif
-
-#define MAX_ARGS 32
-
-struct sram_block
-{
- unsigned type;
- void *data;
- size_t size;
-};
-
-typedef struct content_stream
-{
- uint32_t a;
- const uint8_t *b;
- size_t c;
- uint32_t crc;
-} content_stream_t;
-
-static const struct file_archive_file_backend *stream_backend = NULL;
-static struct string_list *temporary_content = NULL;
-static bool _content_is_inited = false;
-static bool core_does_not_need_content = false;
-static uint32_t content_crc = 0;
-
-#ifdef HAVE_COMPRESSION
-
-#ifdef HAVE_7ZIP
-static bool utf16_to_char(uint8_t **utf_data,
- size_t *dest_len, const uint16_t *in)
-{
- unsigned len = 0;
-
- while (in[len] != '\0')
- len++;
-
- utf16_conv_utf8(NULL, dest_len, in, len);
- *dest_len += 1;
- *utf_data = (uint8_t*)malloc(*dest_len);
- if (*utf_data == 0)
- return false;
-
- return utf16_conv_utf8(*utf_data, dest_len, in, len);
-}
-
-static bool utf16_to_char_string(const uint16_t *in, char *s, size_t len)
-{
- size_t dest_len = 0;
- uint8_t *utf16_data = NULL;
- bool ret = utf16_to_char(&utf16_data, &dest_len, in);
-
- if (ret)
- {
- utf16_data[dest_len] = 0;
- strlcpy(s, (const char*)utf16_data, len);
- }
-
- free(utf16_data);
- utf16_data = NULL;
-
- return ret;
-}
-
-/* Extract the relative path (needle) from a 7z archive
- * (path) and allocate a buf for it to write it in.
- * If optional_outfile is set, extract to that instead
- * and don't allocate buffer.
- */
-static int content_7zip_file_read(
- const char *path,
- const char *needle, void **buf,
- const char *optional_outfile)
-{
- CFileInStream archiveStream;
- CLookToRead lookStream;
- CSzArEx db;
- ISzAlloc allocImp;
- ISzAlloc allocTempImp;
- uint8_t *output = 0;
- long outsize = -1;
-
- /*These are the allocation routines.
- * Currently using the non-standard 7zip choices. */
- allocImp.Alloc = SzAlloc;
- allocImp.Free = SzFree;
- allocTempImp.Alloc = SzAllocTemp;
- allocTempImp.Free = SzFreeTemp;
-
- if (InFile_Open(&archiveStream.file, path))
- {
- RARCH_ERR("Could not open %s as 7z archive\n.", path);
- return -1;
- }
-
- FileInStream_CreateVTable(&archiveStream);
- LookToRead_CreateVTable(&lookStream, False);
- lookStream.realStream = &archiveStream.s;
- LookToRead_Init(&lookStream);
- CrcGenerateTable();
- SzArEx_Init(&db);
-
- if (SzArEx_Open(&db, &lookStream.s, &allocImp, &allocTempImp) == SZ_OK)
- {
- uint32_t i;
- bool file_found = false;
- uint16_t *temp = NULL;
- size_t temp_size = 0;
- uint32_t block_index = 0xFFFFFFFF;
- SRes res = SZ_OK;
-
- for (i = 0; i < db.db.NumFiles; i++)
- {
- size_t len;
- char infile[PATH_MAX_LENGTH];
- size_t offset = 0;
- size_t outSizeProcessed = 0;
- const CSzFileItem *f = db.db.Files + i;
-
- /* We skip over everything which is not a directory.
- * FIXME: Why continue then if f->IsDir is true?*/
- if (f->IsDir)
- continue;
-
- len = SzArEx_GetFileNameUtf16(&db, i, NULL);
-
- if (len > temp_size)
- {
- free(temp);
- temp_size = len;
- temp = (uint16_t *)malloc(temp_size * sizeof(temp[0]));
-
- if (temp == 0)
- {
- res = SZ_ERROR_MEM;
- break;
- }
- }
-
- SzArEx_GetFileNameUtf16(&db, i, temp);
- res = utf16_to_char_string(temp, infile, sizeof(infile))
- ? SZ_OK : SZ_ERROR_FAIL;
-
- if (string_is_equal(infile, needle))
- {
- size_t output_size = 0;
-
- RARCH_LOG_OUTPUT("Opened archive %s. Now trying to extract %s\n",
- path, needle);
-
- /* C LZMA SDK does not support chunked extraction - see here:
- * sourceforge.net/p/sevenzip/discussion/45798/thread/6fb59aaf/
- * */
- file_found = true;
- res = SzArEx_Extract(&db, &lookStream.s, i, &block_index,
- &output, &output_size, &offset, &outSizeProcessed,
- &allocImp, &allocTempImp);
-
- if (res != SZ_OK)
- break; /* This goes to the error section. */
-
- outsize = outSizeProcessed;
-
- if (optional_outfile != NULL)
- {
- const void *ptr = (const void*)(output + offset);
-
- if (!filestream_write_file(optional_outfile, ptr, outsize))
- {
- RARCH_ERR("Could not open outfilepath %s.\n",
- optional_outfile);
- res = SZ_OK;
- file_found = true;
- outsize = -1;
- }
- }
- else
- {
- /*We could either use the 7Zip allocated buffer,
- * or create our own and use it.
- * We would however need to realloc anyways, because RetroArch
- * expects a \0 at the end, therefore we allocate new,
- * copy and free the old one. */
- *buf = malloc(outsize + 1);
- ((char*)(*buf))[outsize] = '\0';
- memcpy(*buf,output + offset,outsize);
- }
- break;
- }
- }
-
- free(temp);
- IAlloc_Free(&allocImp, output);
-
- if (!(file_found && res == SZ_OK))
- {
- /* Error handling */
- if (!file_found)
- RARCH_ERR("File %s not found in %s\n", needle, path);
-
- RARCH_ERR("Failed to open compressed file inside 7zip archive.\n");
-
- outsize = -1;
- }
- }
-
- SzArEx_Free(&db, &allocImp);
- File_Close(&archiveStream.file);
-
- return outsize;
-}
-
-static struct string_list *compressed_7zip_file_list_new(
- const char *path, const char* ext)
-{
- CFileInStream archiveStream;
- CLookToRead lookStream;
- CSzArEx db;
- ISzAlloc allocImp;
- ISzAlloc allocTempImp;
- size_t temp_size = 0;
- struct string_list *list = NULL;
-
- /* These are the allocation routines - currently using
- * the non-standard 7zip choices. */
- allocImp.Alloc = SzAlloc;
- allocImp.Free = SzFree;
- allocTempImp.Alloc = SzAllocTemp;
- allocTempImp.Free = SzFreeTemp;
-
- if (InFile_Open(&archiveStream.file, path))
- {
- RARCH_ERR("Could not open %s as 7z archive.\n",path);
- return NULL;
- }
-
- list = string_list_new();
-
- if (!list)
- {
- File_Close(&archiveStream.file);
- return NULL;
- }
-
- FileInStream_CreateVTable(&archiveStream);
- LookToRead_CreateVTable(&lookStream, False);
- lookStream.realStream = &archiveStream.s;
- LookToRead_Init(&lookStream);
- CrcGenerateTable();
- SzArEx_Init(&db);
-
- if (SzArEx_Open(&db, &lookStream.s, &allocImp, &allocTempImp) == SZ_OK)
- {
- uint32_t i;
- struct string_list *ext_list = ext ? string_split(ext, "|"): NULL;
- SRes res = SZ_OK;
- uint16_t *temp = NULL;
-
- for (i = 0; i < db.db.NumFiles; i++)
- {
- union string_list_elem_attr attr;
- char infile[PATH_MAX_LENGTH];
- const char *file_ext = NULL;
- size_t len = 0;
- bool supported_by_core = false;
- const CSzFileItem *f = db.db.Files + i;
-
- /* we skip over everything, which is a directory. */
- if (f->IsDir)
- continue;
-
- len = SzArEx_GetFileNameUtf16(&db, i, NULL);
-
- if (len > temp_size)
- {
- free(temp);
- temp_size = len;
- temp = (uint16_t *)malloc(temp_size * sizeof(temp[0]));
-
- if (temp == 0)
- {
- res = SZ_ERROR_MEM;
- break;
- }
- }
-
- SzArEx_GetFileNameUtf16(&db, i, temp);
- res = utf16_to_char_string(temp, infile, sizeof(infile))
- ? SZ_OK : SZ_ERROR_FAIL;
- file_ext = path_get_extension(infile);
-
- if (string_list_find_elem_prefix(ext_list, ".", file_ext))
- supported_by_core = true;
-
- /*
- * Currently we only support files without subdirs in the archives.
- * Folders are not supported (differences between win and lin.
- * Archives within archives should imho never be supported.
- */
-
- if (!supported_by_core)
- continue;
-
- attr.i = RARCH_COMPRESSED_FILE_IN_ARCHIVE;
-
- if (!string_list_append(list, infile, attr))
- {
- res = SZ_ERROR_MEM;
- break;
- }
- }
-
- string_list_free(ext_list);
- free(temp);
-
- if (res != SZ_OK)
- {
- /* Error handling */
- RARCH_ERR("Failed to open compressed_file: \"%s\"\n", path);
-
- string_list_free(list);
- list = NULL;
- }
- }
-
- SzArEx_Free(&db, &allocImp);
- File_Close(&archiveStream.file);
-
- return list;
-}
-#endif
-
-#ifdef HAVE_ZLIB
-struct decomp_state
-{
- char *opt_file;
- char *needle;
- void **buf;
- size_t size;
- bool found;
-};
-
-static bool content_zip_file_decompressed_handle(
- file_archive_file_handle_t *handle,
- const uint8_t *cdata, uint32_t csize,
- uint32_t size, uint32_t crc32)
-{
- int ret = 0;
-
- handle->backend = file_archive_get_default_file_backend();
-
- if (!handle->backend)
- goto error;
-
- if (!handle->backend->stream_decompress_data_to_file_init(
- handle, cdata, csize, size))
- return false;
-
- do{
- ret = handle->backend->stream_decompress_data_to_file_iterate(
- handle->stream);
- }while(ret == 0);
-
- handle->real_checksum = handle->backend->stream_crc_calculate(0,
- handle->data, size);
-
- if (handle->real_checksum != crc32)
- {
- RARCH_ERR("Inflated checksum did not match CRC32!\n");
- goto error;
- }
-
- if (handle->stream)
- free(handle->stream);
-
- return true;
-
-error:
- if (handle->stream)
- free(handle->stream);
- if (handle->data)
- free(handle->data);
-
- return false;
-}
-
-/* Extract the relative path (needle) from a
- * ZIP archive (path) and allocate a buffer for it to write it in.
- *
- * optional_outfile if not NULL will be used to extract the file to.
- * buf will be 0 then.
- */
-
-static int content_zip_file_decompressed(
- const char *name, const char *valid_exts,
- const uint8_t *cdata, unsigned cmode,
- uint32_t csize, uint32_t size,
- uint32_t crc32, void *userdata)
-{
- struct decomp_state *st = (struct decomp_state*)userdata;
-
- /* Ignore directories. */
- if (name[strlen(name) - 1] == '/' || name[strlen(name) - 1] == '\\')
- return 1;
-
- RARCH_LOG("[deflate] Path: %s, CRC32: 0x%x\n", name, crc32);
-
- if (strstr(name, st->needle))
- {
- bool goto_error = false;
- file_archive_file_handle_t handle = {0};
-
- st->found = true;
-
- if (content_zip_file_decompressed_handle(&handle,
- cdata, csize, size, crc32))
- {
- if (st->opt_file != 0)
- {
- /* Called in case core has need_fullpath enabled. */
- char *buf = (char*)malloc(size);
-
- if (buf)
- {
- RARCH_LOG("Extracting file : %s\n", st->opt_file);
- memcpy(buf, handle.data, size);
-
- if (!filestream_write_file(st->opt_file, buf, size))
- goto_error = true;
- }
-
- free(buf);
-
- st->size = 0;
- }
- else
- {
- /* Called in case core has need_fullpath disabled.
- * Will copy decompressed content directly into
- * RetroArch's ROM buffer. */
- *st->buf = malloc(size);
- memcpy(*st->buf, handle.data, size);
-
- st->size = size;
- }
- }
-
- if (handle.data)
- free(handle.data);
-
- if (goto_error)
- return 0;
- }
-
- return 1;
-}
-
-static int content_zip_file_read(
- const char *path,
- const char *needle, void **buf,
- const char* optional_outfile)
-{
- file_archive_transfer_t zlib;
- struct decomp_state st;
- bool returnerr = true;
- int ret = 0;
-
- zlib.type = ZLIB_TRANSFER_INIT;
-
- st.needle = NULL;
- st.opt_file = NULL;
- st.found = false;
- st.buf = buf;
-
- if (needle)
- st.needle = strdup(needle);
- if (optional_outfile)
- st.opt_file = strdup(optional_outfile);
-
- do
- {
- ret = file_archive_parse_file_iterate(&zlib, &returnerr, path,
- "", content_zip_file_decompressed, &st);
- if (!returnerr)
- break;
- }while(ret == 0 && !st.found);
-
- file_archive_parse_file_iterate_stop(&zlib);
-
- if (st.opt_file)
- free(st.opt_file);
- if (st.needle)
- free(st.needle);
-
- if (!st.found)
- return -1;
-
- return st.size;
-}
-#endif
-
-#endif
-
-#ifdef HAVE_COMPRESSION
-/* Generic compressed file loader.
- * Extracts to buf, unless optional_filename != 0
- * Then extracts to optional_filename and leaves buf alone.
- */
-static int content_file_compressed_read(
- const char * path, void **buf,
- const char* optional_filename, ssize_t *length)
-{
- int ret = 0;
- const char* file_ext = NULL;
- struct string_list *str_list = string_split(path, "#");
-
- /* Safety check.
- * If optional_filename and optional_filename
- * exists, we simply return 0,
- * hoping that optional_filename is the
- * same as requested.
- */
- if (optional_filename && path_file_exists(optional_filename))
- {
- *length = 0;
- return 1;
- }
-
- /* We assure that there is something after the '#' symbol.
- *
- * This error condition happens for example, when
- * path = /path/to/file.7z, or
- * path = /path/to/file.7z#
- */
- if (str_list->size <= 1)
- goto error;
-
-#if defined(HAVE_7ZIP) || defined(HAVE_ZLIB)
- file_ext = path_get_extension(str_list->elems[0].data);
-#endif
-
-#ifdef HAVE_7ZIP
- if (string_is_equal_noncase(file_ext, "7z"))
- {
- *length = content_7zip_file_read(str_list->elems[0].data,
- str_list->elems[1].data, buf, optional_filename);
- if (*length != -1)
- ret = 1;
- }
-#endif
-#ifdef HAVE_ZLIB
- if (string_is_equal_noncase(file_ext, "zip"))
- {
- *length = content_zip_file_read(str_list->elems[0].data,
- str_list->elems[1].data, buf, optional_filename);
- if (*length != -1)
- ret = 1;
- }
-
- string_list_free(str_list);
-#endif
- return ret;
-
-error:
- RARCH_ERR("Could not extract string and substring from "
- ": %s.\n", path);
- string_list_free(str_list);
- *length = 0;
- return 0;
-}
-#endif
-
-/**
- * content_file_read:
- * @path : path to file.
- * @buf : buffer to allocate and read the contents of the
- * file into. Needs to be freed manually.
- * @length : Number of items read, -1 on error.
- *
- * Read the contents of a file into @buf. Will call content_file_compressed_read
- * if path contains a compressed file, otherwise will call filestream_read_file().
- *
- * Returns: 1 if file read, 0 on error.
- */
-static int content_file_read(const char *path, void **buf, ssize_t *length)
-{
-#ifdef HAVE_COMPRESSION
- if (path_contains_compressed_file(path))
- {
- if (content_file_compressed_read(path, buf, NULL, length))
- return 1;
- }
-#endif
- return filestream_read_file(path, buf, length);
-}
-
-struct string_list *compressed_file_list_new(const char *path,
- const char* ext)
-{
-#if defined(HAVE_ZLIB) || defined(HAVE_7ZIP)
- const char* file_ext = path_get_extension(path);
-#endif
-
-#ifdef HAVE_7ZIP
- if (string_is_equal_noncase(file_ext, "7z"))
- return compressed_7zip_file_list_new(path,ext);
-#endif
-#ifdef HAVE_ZLIB
- if (string_is_equal_noncase(file_ext, "zip"))
- return file_archive_get_file_list(path, ext);
-#endif
- return NULL;
-}
-
-static void check_defaults_dir_create_dir(const char *path)
-{
- char new_path[PATH_MAX_LENGTH];
- fill_pathname_expand_special(new_path,
- path, sizeof(new_path));
-
- if (path_is_directory(new_path))
- return;
- path_mkdir(new_path);
-}
-
-static void check_default_dirs(void)
-{
- /* early return for people with a custom folder setup
- so it doesn't create unnecessary directories
- */
- if (path_file_exists("custom.ini"))
- return;
- if (*g_defaults.dir.core_assets)
- check_defaults_dir_create_dir(g_defaults.dir.core_assets);
- if (*g_defaults.dir.remap)
- check_defaults_dir_create_dir(g_defaults.dir.remap);
- if (*g_defaults.dir.screenshot)
- check_defaults_dir_create_dir(g_defaults.dir.screenshot);
- if (*g_defaults.dir.core)
- check_defaults_dir_create_dir(g_defaults.dir.core);
- if (*g_defaults.dir.autoconfig)
- check_defaults_dir_create_dir(g_defaults.dir.autoconfig);
- if (*g_defaults.dir.audio_filter)
- check_defaults_dir_create_dir(g_defaults.dir.audio_filter);
- if (*g_defaults.dir.video_filter)
- check_defaults_dir_create_dir(g_defaults.dir.video_filter);
- if (*g_defaults.dir.assets)
- check_defaults_dir_create_dir(g_defaults.dir.assets);
- if (*g_defaults.dir.playlist)
- check_defaults_dir_create_dir(g_defaults.dir.playlist);
- if (*g_defaults.dir.core)
- check_defaults_dir_create_dir(g_defaults.dir.core);
- if (*g_defaults.dir.core_info)
- check_defaults_dir_create_dir(g_defaults.dir.core_info);
- if (*g_defaults.dir.overlay)
- check_defaults_dir_create_dir(g_defaults.dir.overlay);
- if (*g_defaults.dir.port)
- check_defaults_dir_create_dir(g_defaults.dir.port);
- if (*g_defaults.dir.shader)
- check_defaults_dir_create_dir(g_defaults.dir.shader);
- if (*g_defaults.dir.savestate)
- check_defaults_dir_create_dir(g_defaults.dir.savestate);
- if (*g_defaults.dir.sram)
- check_defaults_dir_create_dir(g_defaults.dir.sram);
- if (*g_defaults.dir.system)
- check_defaults_dir_create_dir(g_defaults.dir.system);
- if (*g_defaults.dir.resampler)
- check_defaults_dir_create_dir(g_defaults.dir.resampler);
- if (*g_defaults.dir.menu_config)
- check_defaults_dir_create_dir(g_defaults.dir.menu_config);
- if (*g_defaults.dir.content_history)
- check_defaults_dir_create_dir(g_defaults.dir.content_history);
- if (*g_defaults.dir.cache)
- check_defaults_dir_create_dir(g_defaults.dir.cache);
- if (*g_defaults.dir.database)
- check_defaults_dir_create_dir(g_defaults.dir.database);
- if (*g_defaults.dir.cursor)
- check_defaults_dir_create_dir(g_defaults.dir.cursor);
- if (*g_defaults.dir.cheats)
- check_defaults_dir_create_dir(g_defaults.dir.cheats);
- if (*g_defaults.dir.thumbnails)
- check_defaults_dir_create_dir(g_defaults.dir.thumbnails);
-}
-
-void content_push_to_history_playlist(bool do_push,
- const char *path, void *data)
-{
- settings_t *settings = config_get_ptr();
- struct retro_system_info *info = (struct retro_system_info*)data;
-
- /* If the history list is not enabled, early return. */
- if (!settings->history_list_enable)
- return;
- if (!g_defaults.history)
- return;
- if (!do_push)
- return;
-
- playlist_push(g_defaults.history,
- path,
- NULL,
- settings->path.libretro,
- info->library_name,
- NULL,
- NULL);
-}
-
-/**
- * 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)
-{
-#ifdef HAVE_FILE_LOGGER
- int i;
-#endif
-
- *argc = 0;
- argv[(*argc)++] = strdup("retroarch");
-
- if (!args->no_content)
- {
- if (args->content_path)
- {
- RARCH_LOG("Using content: %s.\n", args->content_path);
- argv[(*argc)++] = strdup(args->content_path);
- }
-#ifdef HAVE_MENU
- else
- {
- RARCH_LOG("No content, starting dummy core.\n");
- 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");
-
-#ifdef HAVE_FILE_LOGGER
- for (i = 0; i < *argc; i++)
- RARCH_LOG("arg #%d: %s\n", i, argv[i]);
-#endif
-}
-
-/**
- * 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).
- **/
-bool content_load(content_ctx_info_t *info)
-{
- unsigned i;
- bool retval = true;
- 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 = (struct rarch_main_wrap*)
- calloc(1, sizeof(*wrap_args));
-
- if (!wrap_args)
- return false;
-
- retro_assert(wrap_args);
-
- 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);
- 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;
-
- if (!retroarch_main_init(wrap_args->argc, wrap_args->argv))
- {
- retval = false;
- goto error;
- }
-
-#ifdef HAVE_MENU
- menu_driver_ctl(RARCH_MENU_CTL_SHADER_MANAGER_INIT, NULL);
-#endif
- command_event(CMD_EVENT_HISTORY_INIT, NULL);
- command_event(CMD_EVENT_RESUME, NULL);
- command_event(CMD_EVENT_VIDEO_SET_ASPECT_RATIO, NULL);
-
- check_default_dirs();
-
- frontend_driver_process_args(rarch_argc_ptr, rarch_argv_ptr);
- frontend_driver_content_loaded();
-
-error:
- for (i = 0; i < ARRAY_SIZE(argv_copy); i++)
- free(argv_copy[i]);
- free(wrap_args);
- return retval;
-}
-
-/**
- * read_content_file:
- * @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 read_content_file(unsigned i, const char *path, void **buf,
- ssize_t *length)
-{
-#ifdef HAVE_ZLIB
- content_stream_t stream_info;
- uint32_t *content_crc_ptr = NULL;
-#endif
- uint8_t *ret_buf = NULL;
- global_t *global = global_get_ptr();
-
- RARCH_LOG("%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)
- return true;
-
- /* Attempt to apply a patch. */
- if (!global->patch.block_patch)
- patch_content(&ret_buf, length);
-
-#ifdef HAVE_ZLIB
- content_get_crc(&content_crc_ptr);
-
- stream_info.a = 0;
- stream_info.b = ret_buf;
- stream_info.c = *length;
-
- if (!stream_backend)
- stream_backend = file_archive_get_default_file_backend();
- stream_info.crc = stream_backend->stream_crc_calculate(
- stream_info.a, stream_info.b, stream_info.c);
- *content_crc_ptr = stream_info.crc;
-
- RARCH_LOG("CRC32: 0x%x .\n", (unsigned)*content_crc_ptr);
-#endif
- *buf = ret_buf;
-
- return true;
-}
-
-/**
- * dump_to_file_desperate:
- * @data : pointer to data buffer.
- * @size : size of @data.
- * @type : type of file to be saved.
- *
- * Attempt to save valuable RAM data somewhere.
- **/
-static bool dump_to_file_desperate(const void *data,
- size_t size, unsigned type)
-{
- time_t time_;
- char timebuf[256];
- char application_data[PATH_MAX_LENGTH];
- char path[PATH_MAX_LENGTH];
-
- if (!fill_pathname_application_data(application_data,
- sizeof(application_data)))
- return false;
-
- snprintf(path, sizeof(path), "%s/RetroArch-recovery-%u",
- application_data, type);
-
- time(&time_);
-
- strftime(timebuf, sizeof(timebuf), "%Y-%m-%d-%H-%M-%S", localtime(&time_));
- strlcat(path, timebuf, sizeof(path));
-
- if (!filestream_write_file(path, data, size))
- return false;
-
- RARCH_WARN("Succeeded in saving RAM data to \"%s\".\n", path);
- return true;
-}
-
-
-/**
- * save_state:
- * @path : path of saved state that shall be written to.
- *
- * Save a state from memory to disk.
- *
- * Returns: true if successful, false otherwise.
- **/
-bool content_save_state(const char *path)
-{
- retro_ctx_serialize_info_t serial_info;
- retro_ctx_size_info_t info;
- bool ret = false;
- void *data = NULL;
-
- core_serialize_size(&info);
-
- RARCH_LOG("%s: \"%s\".\n",
- msg_hash_to_str(MSG_SAVING_STATE),
- path);
-
- if (info.size == 0)
- return false;
-
- data = malloc(info.size);
-
- if (!data)
- return false;
-
- RARCH_LOG("%s: %d %s.\n",
- msg_hash_to_str(MSG_STATE_SIZE),
- (int)info.size,
- msg_hash_to_str(MSG_BYTES));
-
- serial_info.data = data;
- serial_info.size = info.size;
- ret = core_serialize(&serial_info);
-
- if (ret)
- ret = filestream_write_file(path, data, info.size);
- else
- {
- RARCH_ERR("%s \"%s\".\n",
- msg_hash_to_str(MSG_FAILED_TO_SAVE_STATE_TO),
- path);
- }
-
- free(data);
-
- return ret;
-}
-
-/**
- * content_load_state:
- * @path : path that state will be loaded from.
- *
- * Load a state from disk to memory.
- *
- * Returns: true if successful, false otherwise.
- **/
-bool content_load_state(const char *path)
-{
- unsigned i;
- ssize_t size;
- retro_ctx_serialize_info_t serial_info;
- unsigned num_blocks = 0;
- void *buf = NULL;
- struct sram_block *blocks = NULL;
- settings_t *settings = config_get_ptr();
- global_t *global = global_get_ptr();
- bool ret = filestream_read_file(path, &buf, &size);
-
- RARCH_LOG("%s: \"%s\".\n",
- msg_hash_to_str(MSG_LOADING_STATE),
- path);
-
- if (!ret || size < 0)
- goto error;
-
- RARCH_LOG("%s: %u %s.\n",
- msg_hash_to_str(MSG_STATE_SIZE),
- (unsigned)size,
- msg_hash_to_str(MSG_BYTES));
-
- if (settings->block_sram_overwrite && global->savefiles
- && global->savefiles->size)
- {
- RARCH_LOG("%s.\n",
- msg_hash_to_str(MSG_BLOCKING_SRAM_OVERWRITE));
- blocks = (struct sram_block*)
- calloc(global->savefiles->size, sizeof(*blocks));
-
- if (blocks)
- {
- num_blocks = global->savefiles->size;
- for (i = 0; i < num_blocks; i++)
- blocks[i].type = global->savefiles->elems[i].attr.i;
- }
- }
-
-
- for (i = 0; i < num_blocks; i++)
- {
- retro_ctx_memory_info_t mem_info;
-
- mem_info.id = blocks[i].type;
- core_get_memory(&mem_info);
-
- blocks[i].size = mem_info.size;
- }
-
- for (i = 0; i < num_blocks; i++)
- if (blocks[i].size)
- blocks[i].data = malloc(blocks[i].size);
-
- /* Backup current SRAM which is overwritten by unserialize. */
- for (i = 0; i < num_blocks; i++)
- {
- if (blocks[i].data)
- {
- retro_ctx_memory_info_t mem_info;
- const void *ptr = NULL;
-
- mem_info.id = blocks[i].type;
-
- core_get_memory(&mem_info);
-
- ptr = mem_info.data;
- if (ptr)
- memcpy(blocks[i].data, ptr, blocks[i].size);
- }
- }
-
- serial_info.data_const = buf;
- serial_info.size = size;
- ret = core_unserialize(&serial_info);
-
- /* Flush back. */
- for (i = 0; i < num_blocks; i++)
- {
- if (blocks[i].data)
- {
- retro_ctx_memory_info_t mem_info;
- void *ptr = NULL;
-
- mem_info.id = blocks[i].type;
-
- core_get_memory(&mem_info);
-
- ptr = mem_info.data;
- if (ptr)
- memcpy(ptr, blocks[i].data, blocks[i].size);
- }
- }
-
- for (i = 0; i < num_blocks; i++)
- free(blocks[i].data);
- free(blocks);
- free(buf);
-
- if (!ret)
- goto error;
-
- return true;
-
-error:
- RARCH_ERR("%s \"%s\".\n",
- msg_hash_to_str(MSG_FAILED_TO_LOAD_STATE),
- path);
- return false;
-}
-
-/**
- * content_load_ram_file:
- * @path : path of RAM state that will be loaded from.
- * @type : type of memory
- *
- * Load a RAM state from disk to memory.
- */
-bool content_load_ram_file(ram_type_t *ram)
-{
- ssize_t rc;
- retro_ctx_memory_info_t mem_info;
- void *buf = NULL;
-
- if (!ram)
- return false;
-
- mem_info.id = ram->type;
-
- core_get_memory(&mem_info);
-
- if (mem_info.size == 0 || !mem_info.data)
- return false;
-
- if (!filestream_read_file(ram->path, &buf, &rc))
- return false;
-
- if (rc > 0)
- {
- if (rc > (ssize_t)mem_info.size)
- {
- RARCH_WARN("SRAM is larger than implementation expects, "
- "doing partial load (truncating %u %s %s %u).\n",
- (unsigned)rc,
- msg_hash_to_str(MSG_BYTES),
- msg_hash_to_str(MSG_TO),
- (unsigned)mem_info.size);
- rc = mem_info.size;
- }
- memcpy(mem_info.data, buf, rc);
- }
-
- if (buf)
- free(buf);
-
- return true;
-}
-
-/**
- * content_save_ram_file:
- * @path : path of RAM state that shall be written to.
- * @type : type of memory
- *
- * Save a RAM state from memory to disk.
- *
- */
-bool content_save_ram_file(ram_type_t *ram)
-{
- retro_ctx_memory_info_t mem_info;
-
- if (!ram)
- return false;
-
- mem_info.id = ram->type;
-
- core_get_memory(&mem_info);
-
- if (!mem_info.data || mem_info.size == 0)
- return false;
-
- if (!filestream_write_file(ram->path, mem_info.data, mem_info.size))
- {
- RARCH_ERR("%s.\n",
- msg_hash_to_str(MSG_FAILED_TO_SAVE_SRAM));
- RARCH_WARN("Attempting to recover ...\n");
-
- /* In case the file could not be written to,
- * the fallback function 'dump_to_file_desperate'
- * will be called. */
- if (!dump_to_file_desperate(mem_info.data, mem_info.size, ram->type))
- {
- RARCH_WARN("Failed ... Cannot recover save file.\n");
- }
- return false;
- }
-
- RARCH_LOG("%s \"%s\".\n",
- msg_hash_to_str(MSG_SAVED_SUCCESSFULLY_TO),
- ram->path);
-
- return true;
-}
-
-/* Load the content into memory. */
-static bool load_content_into_memory(
- struct retro_game_info *info,
- unsigned i,
- const char *path)
-{
- ssize_t len;
- bool ret = false;
-
- if (i == 0)
- {
- /* First content file is significant, attempt to do patching,
- * CRC checking, etc. */
- ret = read_content_file(i, path, (void**)&info->data, &len);
- }
- else
- ret = content_file_read(path, (void**)&info->data, &len);
-
- if (!ret || len < 0)
- goto error;
-
- info->size = len;
-
- return true;
-
-error:
- RARCH_ERR("%s \"%s\".\n",
- msg_hash_to_str(MSG_COULD_NOT_READ_CONTENT_FILE),
- path);
- return false;
-}
-
-#ifdef HAVE_COMPRESSION
-static bool load_content_from_compressed_archive(
- struct string_list *temporary_content,
- struct retro_game_info *info, unsigned i,
- struct string_list* additional_path_allocs,
- bool need_fullpath, const char *path)
-{
- union string_list_elem_attr attributes;
- char new_path[PATH_MAX_LENGTH];
- char new_basedir[PATH_MAX_LENGTH];
- ssize_t new_path_len = 0;
- bool ret = false;
- settings_t *settings = config_get_ptr();
- rarch_system_info_t *sys_info= NULL;
-
- runloop_ctl(RUNLOOP_CTL_SYSTEM_INFO_GET, &sys_info);
-
- if (sys_info && sys_info->info.block_extract)
- return true;
- if (!need_fullpath || !path_contains_compressed_file(path))
- return true;
-
- RARCH_LOG("Compressed file in case of need_fullpath."
- " Now extracting to temporary directory.\n");
-
- strlcpy(new_basedir, settings->directory.cache,
- sizeof(new_basedir));
-
- if (string_is_empty(new_basedir) || !path_is_directory(new_basedir))
- {
- RARCH_WARN("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));
- }
-
- attributes.i = 0;
- fill_pathname_join(new_path, new_basedir,
- path_basename(path), sizeof(new_path));
-
- ret = content_file_compressed_read(path, NULL, new_path, &new_path_len);
-
- if (!ret || new_path_len < 0)
- {
- RARCH_ERR("%s \"%s\".\n",
- msg_hash_to_str(MSG_COULD_NOT_READ_CONTENT_FILE),
- path);
- return false;
- }
-
- RARCH_LOG("New path is: [%s]\n", new_path);
-
- string_list_append(additional_path_allocs, new_path, attributes);
- info[i].path =
- additional_path_allocs->elems[additional_path_allocs->size -1 ].data;
-
- if (!string_list_append(temporary_content, new_path, attributes))
- return false;
-
- return true;
-}
-#endif
-
-/**
- * load_content:
- * @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 load_content(
- struct string_list *temporary_content,
- struct retro_game_info *info,
- const struct string_list *content,
- const struct retro_subsystem_info *special,
- struct string_list* additional_path_allocs
- )
-{
- unsigned i;
- retro_ctx_load_content_info_t load_info;
-
- if (!info || !additional_path_allocs)
- return false;
-
- for (i = 0; i < content->size; i++)
- {
- int attr = content->elems[i].attr.i;
- bool need_fullpath = attr & 2;
- bool require_content = attr & 4;
- const char *path = content->elems[i].data;
-
- if (require_content && string_is_empty(path))
- {
- RARCH_LOG("libretro core requires content, "
- "but nothing was provided.\n");
- return false;
- }
-
- info[i].path = NULL;
-
- if (*path)
- info[i].path = path;
-
- if (!need_fullpath && !string_is_empty(path))
- {
- if (!load_content_into_memory(&info[i], i, path))
- return false;
- }
- else
- {
- RARCH_LOG("Content loading skipped. Implementation will"
- " load it on its own.\n");
-
-#ifdef HAVE_COMPRESSION
- if (!load_content_from_compressed_archive(
- temporary_content,
- &info[i], i,
- additional_path_allocs, need_fullpath, path))
- return false;
-#endif
- }
- }
-
- load_info.content = content;
- load_info.special = special;
- load_info.info = info;
-
- if (!core_load_game(&load_info))
- {
- RARCH_ERR("%s.\n", msg_hash_to_str(MSG_FAILED_TO_LOAD_CONTENT));
- return false;
- }
-
-#ifdef HAVE_CHEEVOS
- if (!special)
- {
- const void *load_data = NULL;
-
- cheevos_set_cheats();
-
- if (*content->elems[0].data)
- load_data = info;
- cheevos_load(load_data);
- }
-#endif
-
- return true;
-}
-
-static const struct retro_subsystem_info *init_content_file_subsystem(
- bool *ret, rarch_system_info_t *system)
-{
- global_t *global = global_get_ptr();
- const struct retro_subsystem_info *special =
- libretro_find_subsystem_info(system->subsystem.data,
- system->subsystem.size, global->subsystem);
-
- if (!special)
- {
- RARCH_ERR(
- "Failed to find subsystem \"%s\" in libretro implementation.\n",
- global->subsystem);
- goto error;
- }
-
- if (special->num_roms && !global->subsystem_fullpaths)
- {
- RARCH_ERR("libretro core requires special content, "
- "but none were provided.\n");
- goto error;
- }
- else if (special->num_roms && special->num_roms
- != global->subsystem_fullpaths->size)
- {
- RARCH_ERR("libretro core requires %u content files for "
- "subsystem \"%s\", but %u content files were provided.\n",
- special->num_roms, special->desc,
- (unsigned)global->subsystem_fullpaths->size);
- goto error;
- }
- else if (!special->num_roms && global->subsystem_fullpaths
- && global->subsystem_fullpaths->size)
- {
- RARCH_ERR("libretro core takes no content for subsystem \"%s\", "
- "but %u content files were provided.\n",
- special->desc,
- (unsigned)global->subsystem_fullpaths->size);
- goto error;
- }
-
- *ret = true;
- return special;
-
-error:
- *ret = false;
- return NULL;
-}
-
-#ifdef HAVE_ZLIB
-static bool init_content_file_extract(
- struct string_list *temporary_content,
- struct string_list *content,
- rarch_system_info_t *system,
- const struct retro_subsystem_info *special,
- union string_list_elem_attr *attr
- )
-{
- unsigned i;
- settings_t *settings = config_get_ptr();
-
- for (i = 0; i < content->size; i++)
- {
- const char *ext = NULL;
- const char *valid_ext = system->info.valid_extensions;
-
- /* Block extract check. */
- if (content->elems[i].attr.i & 1)
- continue;
-
- ext = path_get_extension(content->elems[i].data);
-
- if (special)
- valid_ext = special->roms[i].valid_extensions;
-
- if (!ext)
- continue;
-
- if (string_is_equal_noncase(ext, "zip"))
- {
- char new_path[PATH_MAX_LENGTH];
- char temp_content[PATH_MAX_LENGTH];
-
- strlcpy(temp_content, content->elems[i].data,
- sizeof(temp_content));
-
- if (!file_archive_extract_first_content_file(temp_content,
- sizeof(temp_content), valid_ext,
- *settings->directory.cache ?
- settings->directory.cache : NULL,
- new_path, sizeof(new_path)))
- {
- RARCH_ERR("Failed to extract content from zipped file: %s.\n",
- temp_content);
- return false;
- }
-
- string_list_set(content, i, new_path);
- if (!string_list_append(temporary_content,
- new_path, *attr))
- return false;
- }
- }
-
- return true;
-}
-#endif
-
-static bool init_content_file_set_attribs(
- struct string_list *temporary_content,
- struct string_list *content,
- rarch_system_info_t *system,
- const struct retro_subsystem_info *special)
-{
- union string_list_elem_attr attr;
- global_t *global = global_get_ptr();
-
- attr.i = 0;
-
- if (*global->subsystem)
- {
- unsigned i;
-
- for (i = 0; i < global->subsystem_fullpaths->size; i++)
- {
- attr.i = special->roms[i].block_extract;
- attr.i |= special->roms[i].need_fullpath << 1;
- attr.i |= special->roms[i].required << 2;
-
- string_list_append(content,
- global->subsystem_fullpaths->elems[i].data, attr);
- }
- }
- else
- {
- settings_t *settings = config_get_ptr();
-
- attr.i = system->info.block_extract;
- attr.i |= system->info.need_fullpath << 1;
- attr.i |= (!content_does_not_need_content()) << 2;
-
- if (content_does_not_need_content()
- && settings->set_supports_no_game_enable)
- string_list_append(content, "", attr);
- else
- {
- char *fullpath = NULL;
- runloop_ctl(RUNLOOP_CTL_GET_CONTENT_PATH, &fullpath);
-
- string_list_append(content, fullpath, attr);
- }
- }
-
-#ifdef HAVE_ZLIB
- /* Try to extract all content we're going to load if appropriate. */
- if (!init_content_file_extract(temporary_content,
- content, system, special, &attr))
- return false;
-#endif
- return true;
-}
-
-/**
- * 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(struct string_list *temporary_content)
-{
- unsigned i;
- struct retro_game_info *info = NULL;
- bool ret = false;
- struct string_list* additional_path_allocs = NULL;
- struct string_list *content = NULL;
- const struct retro_subsystem_info *special = NULL;
- rarch_system_info_t *system = NULL;
- global_t *global = global_get_ptr();
-
- runloop_ctl(RUNLOOP_CTL_SYSTEM_INFO_GET, &system);
-
- if (*global->subsystem)
- {
- special = init_content_file_subsystem(&ret, system);
- if (!ret)
- goto error;
- }
-
- content = string_list_new();
-
- if (!content)
- goto error;
-
- if (!init_content_file_set_attribs(temporary_content,
- content, system, special))
- goto error;
-
- info = (struct retro_game_info*)
- calloc(content->size, sizeof(*info));
- additional_path_allocs = string_list_new();
-
- ret = load_content(temporary_content,
- info, content, special, additional_path_allocs);
-
- for (i = 0; i < content->size; i++)
- free((void*)info[i].data);
-
- string_list_free(additional_path_allocs);
- if (info)
- free(info);
-
-error:
- if (content)
- string_list_free(content);
- return ret;
-}
-
-static bool content_file_free(struct string_list *temporary_content)
-{
- unsigned i;
-
- if (!temporary_content)
- return false;
-
- for (i = 0; i < temporary_content->size; i++)
- {
- const char *path = temporary_content->elems[i].data;
-
- RARCH_LOG("%s: %s.\n",
- msg_hash_to_str(MSG_REMOVING_TEMPORARY_CONTENT_FILE), path);
- if (remove(path) < 0)
- RARCH_ERR("%s: %s.\n",
- msg_hash_to_str(MSG_FAILED_TO_REMOVE_TEMPORARY_FILE),
- path);
- }
- string_list_free(temporary_content);
-
- return true;
-}
-
-bool content_does_not_need_content(void)
-{
- return core_does_not_need_content;
-}
-
-void content_set_does_not_need_content(void)
-{
- core_does_not_need_content = true;
-}
-
-void content_unset_does_not_need_content(void)
-{
- core_does_not_need_content = false;
-}
-
-bool content_get_crc(uint32_t **content_crc_ptr)
-{
- if (!content_crc_ptr)
- return false;
- *content_crc_ptr = &content_crc;
- return true;
-}
-
-bool content_is_inited(void)
-{
- return _content_is_inited;
-}
-
-void content_deinit(void)
-{
- content_file_free(temporary_content);
- temporary_content = NULL;
- content_crc = 0;
- _content_is_inited = false;
- core_does_not_need_content = false;
-}
-
-/* Initializes and loads a content file for the currently
- * selected libretro core. */
-bool content_init(void)
-{
- temporary_content = string_list_new();
- if (!temporary_content)
- goto error;
-
- if (!content_file_init(temporary_content))
- goto error;
-
- _content_is_inited = true;
- return true;
-
-error:
- content_deinit();
- return false;
-}
diff --git a/griffin/griffin.c b/griffin/griffin.c
index 618a6cb4c7..f8431ba83f 100644
--- a/griffin/griffin.c
+++ b/griffin/griffin.c
@@ -649,7 +649,6 @@ CORES
/*============================================================
FILE
============================================================ */
-#include "../content.c"
#include "../libretro-common/file/file_path.c"
#include "../file_path_special.c"
#include "../libretro-common/lists/dir_list.c"
@@ -1000,4 +999,4 @@ XML
#ifdef __cplusplus
}
-#endif
\ No newline at end of file
+#endif
diff --git a/tasks/task_content.c b/tasks/task_content.c
index 7fd79842cf..d30fb7d664 100644
--- a/tasks/task_content.c
+++ b/tasks/task_content.c
@@ -13,7 +13,13 @@
* If not, see .
*/
+#include
+#include
#include
+#include
+#include
+#include
+#include
#include
@@ -33,6 +39,1750 @@
#include "../retroarch.h"
#include "../verbosity.h"
+#ifdef _WIN32
+#ifdef _XBOX
+#include
+#define setmode _setmode
+#define INVALID_FILE_ATTRIBUTES -1
+#else
+#include
+#include
+#include
+#endif
+#endif
+
+#include
+
+#include
+#include
+#include
+#include
+#ifdef HAVE_COMPRESSION
+#include
+#endif
+#include
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include "../defaults.h"
+#include "../msg_hash.h"
+#include "../content.h"
+#include "../general.h"
+#include "../dynamic.h"
+#include "../movie.h"
+#include "../patch.h"
+#include "../system.h"
+#include "../retroarch.h"
+#include "../command.h"
+#include "../file_path_special.h"
+#include "../core.h"
+#include "../verbosity.h"
+
+#ifdef HAVE_7ZIP
+#include "../deps/7zip/7z.h"
+#include "../deps/7zip/7zAlloc.h"
+#include "../deps/7zip/7zCrc.h"
+#include "../deps/7zip/7zFile.h"
+#endif
+
+#ifdef HAVE_MENU
+#include "../menu/menu_driver.h"
+#endif
+
+#ifdef HAVE_CHEEVOS
+#include "../cheevos.h"
+#endif
+
+#define MAX_ARGS 32
+
+struct sram_block
+{
+ unsigned type;
+ void *data;
+ size_t size;
+};
+
+typedef struct content_stream
+{
+ uint32_t a;
+ const uint8_t *b;
+ size_t c;
+ uint32_t crc;
+} content_stream_t;
+
+static const struct file_archive_file_backend *stream_backend = NULL;
+static struct string_list *temporary_content = NULL;
+static bool _content_is_inited = false;
+static bool core_does_not_need_content = false;
+static uint32_t content_crc = 0;
+
+#ifdef HAVE_COMPRESSION
+
+#ifdef HAVE_7ZIP
+static bool utf16_to_char(uint8_t **utf_data,
+ size_t *dest_len, const uint16_t *in)
+{
+ unsigned len = 0;
+
+ while (in[len] != '\0')
+ len++;
+
+ utf16_conv_utf8(NULL, dest_len, in, len);
+ *dest_len += 1;
+ *utf_data = (uint8_t*)malloc(*dest_len);
+ if (*utf_data == 0)
+ return false;
+
+ return utf16_conv_utf8(*utf_data, dest_len, in, len);
+}
+
+static bool utf16_to_char_string(const uint16_t *in, char *s, size_t len)
+{
+ size_t dest_len = 0;
+ uint8_t *utf16_data = NULL;
+ bool ret = utf16_to_char(&utf16_data, &dest_len, in);
+
+ if (ret)
+ {
+ utf16_data[dest_len] = 0;
+ strlcpy(s, (const char*)utf16_data, len);
+ }
+
+ free(utf16_data);
+ utf16_data = NULL;
+
+ return ret;
+}
+
+/* Extract the relative path (needle) from a 7z archive
+ * (path) and allocate a buf for it to write it in.
+ * If optional_outfile is set, extract to that instead
+ * and don't allocate buffer.
+ */
+static int content_7zip_file_read(
+ const char *path,
+ const char *needle, void **buf,
+ const char *optional_outfile)
+{
+ CFileInStream archiveStream;
+ CLookToRead lookStream;
+ CSzArEx db;
+ ISzAlloc allocImp;
+ ISzAlloc allocTempImp;
+ uint8_t *output = 0;
+ long outsize = -1;
+
+ /*These are the allocation routines.
+ * Currently using the non-standard 7zip choices. */
+ allocImp.Alloc = SzAlloc;
+ allocImp.Free = SzFree;
+ allocTempImp.Alloc = SzAllocTemp;
+ allocTempImp.Free = SzFreeTemp;
+
+ if (InFile_Open(&archiveStream.file, path))
+ {
+ RARCH_ERR("Could not open %s as 7z archive\n.", path);
+ return -1;
+ }
+
+ FileInStream_CreateVTable(&archiveStream);
+ LookToRead_CreateVTable(&lookStream, False);
+ lookStream.realStream = &archiveStream.s;
+ LookToRead_Init(&lookStream);
+ CrcGenerateTable();
+ SzArEx_Init(&db);
+
+ if (SzArEx_Open(&db, &lookStream.s, &allocImp, &allocTempImp) == SZ_OK)
+ {
+ uint32_t i;
+ bool file_found = false;
+ uint16_t *temp = NULL;
+ size_t temp_size = 0;
+ uint32_t block_index = 0xFFFFFFFF;
+ SRes res = SZ_OK;
+
+ for (i = 0; i < db.db.NumFiles; i++)
+ {
+ size_t len;
+ char infile[PATH_MAX_LENGTH];
+ size_t offset = 0;
+ size_t outSizeProcessed = 0;
+ const CSzFileItem *f = db.db.Files + i;
+
+ /* We skip over everything which is not a directory.
+ * FIXME: Why continue then if f->IsDir is true?*/
+ if (f->IsDir)
+ continue;
+
+ len = SzArEx_GetFileNameUtf16(&db, i, NULL);
+
+ if (len > temp_size)
+ {
+ free(temp);
+ temp_size = len;
+ temp = (uint16_t *)malloc(temp_size * sizeof(temp[0]));
+
+ if (temp == 0)
+ {
+ res = SZ_ERROR_MEM;
+ break;
+ }
+ }
+
+ SzArEx_GetFileNameUtf16(&db, i, temp);
+ res = utf16_to_char_string(temp, infile, sizeof(infile))
+ ? SZ_OK : SZ_ERROR_FAIL;
+
+ if (string_is_equal(infile, needle))
+ {
+ size_t output_size = 0;
+
+ RARCH_LOG_OUTPUT("Opened archive %s. Now trying to extract %s\n",
+ path, needle);
+
+ /* C LZMA SDK does not support chunked extraction - see here:
+ * sourceforge.net/p/sevenzip/discussion/45798/thread/6fb59aaf/
+ * */
+ file_found = true;
+ res = SzArEx_Extract(&db, &lookStream.s, i, &block_index,
+ &output, &output_size, &offset, &outSizeProcessed,
+ &allocImp, &allocTempImp);
+
+ if (res != SZ_OK)
+ break; /* This goes to the error section. */
+
+ outsize = outSizeProcessed;
+
+ if (optional_outfile != NULL)
+ {
+ const void *ptr = (const void*)(output + offset);
+
+ if (!filestream_write_file(optional_outfile, ptr, outsize))
+ {
+ RARCH_ERR("Could not open outfilepath %s.\n",
+ optional_outfile);
+ res = SZ_OK;
+ file_found = true;
+ outsize = -1;
+ }
+ }
+ else
+ {
+ /*We could either use the 7Zip allocated buffer,
+ * or create our own and use it.
+ * We would however need to realloc anyways, because RetroArch
+ * expects a \0 at the end, therefore we allocate new,
+ * copy and free the old one. */
+ *buf = malloc(outsize + 1);
+ ((char*)(*buf))[outsize] = '\0';
+ memcpy(*buf,output + offset,outsize);
+ }
+ break;
+ }
+ }
+
+ free(temp);
+ IAlloc_Free(&allocImp, output);
+
+ if (!(file_found && res == SZ_OK))
+ {
+ /* Error handling */
+ if (!file_found)
+ RARCH_ERR("File %s not found in %s\n", needle, path);
+
+ RARCH_ERR("Failed to open compressed file inside 7zip archive.\n");
+
+ outsize = -1;
+ }
+ }
+
+ SzArEx_Free(&db, &allocImp);
+ File_Close(&archiveStream.file);
+
+ return outsize;
+}
+
+static struct string_list *compressed_7zip_file_list_new(
+ const char *path, const char* ext)
+{
+ CFileInStream archiveStream;
+ CLookToRead lookStream;
+ CSzArEx db;
+ ISzAlloc allocImp;
+ ISzAlloc allocTempImp;
+ size_t temp_size = 0;
+ struct string_list *list = NULL;
+
+ /* These are the allocation routines - currently using
+ * the non-standard 7zip choices. */
+ allocImp.Alloc = SzAlloc;
+ allocImp.Free = SzFree;
+ allocTempImp.Alloc = SzAllocTemp;
+ allocTempImp.Free = SzFreeTemp;
+
+ if (InFile_Open(&archiveStream.file, path))
+ {
+ RARCH_ERR("Could not open %s as 7z archive.\n",path);
+ return NULL;
+ }
+
+ list = string_list_new();
+
+ if (!list)
+ {
+ File_Close(&archiveStream.file);
+ return NULL;
+ }
+
+ FileInStream_CreateVTable(&archiveStream);
+ LookToRead_CreateVTable(&lookStream, False);
+ lookStream.realStream = &archiveStream.s;
+ LookToRead_Init(&lookStream);
+ CrcGenerateTable();
+ SzArEx_Init(&db);
+
+ if (SzArEx_Open(&db, &lookStream.s, &allocImp, &allocTempImp) == SZ_OK)
+ {
+ uint32_t i;
+ struct string_list *ext_list = ext ? string_split(ext, "|"): NULL;
+ SRes res = SZ_OK;
+ uint16_t *temp = NULL;
+
+ for (i = 0; i < db.db.NumFiles; i++)
+ {
+ union string_list_elem_attr attr;
+ char infile[PATH_MAX_LENGTH];
+ const char *file_ext = NULL;
+ size_t len = 0;
+ bool supported_by_core = false;
+ const CSzFileItem *f = db.db.Files + i;
+
+ /* we skip over everything, which is a directory. */
+ if (f->IsDir)
+ continue;
+
+ len = SzArEx_GetFileNameUtf16(&db, i, NULL);
+
+ if (len > temp_size)
+ {
+ free(temp);
+ temp_size = len;
+ temp = (uint16_t *)malloc(temp_size * sizeof(temp[0]));
+
+ if (temp == 0)
+ {
+ res = SZ_ERROR_MEM;
+ break;
+ }
+ }
+
+ SzArEx_GetFileNameUtf16(&db, i, temp);
+ res = utf16_to_char_string(temp, infile, sizeof(infile))
+ ? SZ_OK : SZ_ERROR_FAIL;
+ file_ext = path_get_extension(infile);
+
+ if (string_list_find_elem_prefix(ext_list, ".", file_ext))
+ supported_by_core = true;
+
+ /*
+ * Currently we only support files without subdirs in the archives.
+ * Folders are not supported (differences between win and lin.
+ * Archives within archives should imho never be supported.
+ */
+
+ if (!supported_by_core)
+ continue;
+
+ attr.i = RARCH_COMPRESSED_FILE_IN_ARCHIVE;
+
+ if (!string_list_append(list, infile, attr))
+ {
+ res = SZ_ERROR_MEM;
+ break;
+ }
+ }
+
+ string_list_free(ext_list);
+ free(temp);
+
+ if (res != SZ_OK)
+ {
+ /* Error handling */
+ RARCH_ERR("Failed to open compressed_file: \"%s\"\n", path);
+
+ string_list_free(list);
+ list = NULL;
+ }
+ }
+
+ SzArEx_Free(&db, &allocImp);
+ File_Close(&archiveStream.file);
+
+ return list;
+}
+#endif
+
+#ifdef HAVE_ZLIB
+struct decomp_state
+{
+ char *opt_file;
+ char *needle;
+ void **buf;
+ size_t size;
+ bool found;
+};
+
+static bool content_zip_file_decompressed_handle(
+ file_archive_file_handle_t *handle,
+ const uint8_t *cdata, uint32_t csize,
+ uint32_t size, uint32_t crc32)
+{
+ int ret = 0;
+
+ handle->backend = file_archive_get_default_file_backend();
+
+ if (!handle->backend)
+ goto error;
+
+ if (!handle->backend->stream_decompress_data_to_file_init(
+ handle, cdata, csize, size))
+ return false;
+
+ do{
+ ret = handle->backend->stream_decompress_data_to_file_iterate(
+ handle->stream);
+ }while(ret == 0);
+
+ handle->real_checksum = handle->backend->stream_crc_calculate(0,
+ handle->data, size);
+
+ if (handle->real_checksum != crc32)
+ {
+ RARCH_ERR("Inflated checksum did not match CRC32!\n");
+ goto error;
+ }
+
+ if (handle->stream)
+ free(handle->stream);
+
+ return true;
+
+error:
+ if (handle->stream)
+ free(handle->stream);
+ if (handle->data)
+ free(handle->data);
+
+ return false;
+}
+
+/* Extract the relative path (needle) from a
+ * ZIP archive (path) and allocate a buffer for it to write it in.
+ *
+ * optional_outfile if not NULL will be used to extract the file to.
+ * buf will be 0 then.
+ */
+
+static int content_zip_file_decompressed(
+ const char *name, const char *valid_exts,
+ const uint8_t *cdata, unsigned cmode,
+ uint32_t csize, uint32_t size,
+ uint32_t crc32, void *userdata)
+{
+ struct decomp_state *st = (struct decomp_state*)userdata;
+
+ /* Ignore directories. */
+ if (name[strlen(name) - 1] == '/' || name[strlen(name) - 1] == '\\')
+ return 1;
+
+ RARCH_LOG("[deflate] Path: %s, CRC32: 0x%x\n", name, crc32);
+
+ if (strstr(name, st->needle))
+ {
+ bool goto_error = false;
+ file_archive_file_handle_t handle = {0};
+
+ st->found = true;
+
+ if (content_zip_file_decompressed_handle(&handle,
+ cdata, csize, size, crc32))
+ {
+ if (st->opt_file != 0)
+ {
+ /* Called in case core has need_fullpath enabled. */
+ char *buf = (char*)malloc(size);
+
+ if (buf)
+ {
+ RARCH_LOG("Extracting file : %s\n", st->opt_file);
+ memcpy(buf, handle.data, size);
+
+ if (!filestream_write_file(st->opt_file, buf, size))
+ goto_error = true;
+ }
+
+ free(buf);
+
+ st->size = 0;
+ }
+ else
+ {
+ /* Called in case core has need_fullpath disabled.
+ * Will copy decompressed content directly into
+ * RetroArch's ROM buffer. */
+ *st->buf = malloc(size);
+ memcpy(*st->buf, handle.data, size);
+
+ st->size = size;
+ }
+ }
+
+ if (handle.data)
+ free(handle.data);
+
+ if (goto_error)
+ return 0;
+ }
+
+ return 1;
+}
+
+static int content_zip_file_read(
+ const char *path,
+ const char *needle, void **buf,
+ const char* optional_outfile)
+{
+ file_archive_transfer_t zlib;
+ struct decomp_state st;
+ bool returnerr = true;
+ int ret = 0;
+
+ zlib.type = ZLIB_TRANSFER_INIT;
+
+ st.needle = NULL;
+ st.opt_file = NULL;
+ st.found = false;
+ st.buf = buf;
+
+ if (needle)
+ st.needle = strdup(needle);
+ if (optional_outfile)
+ st.opt_file = strdup(optional_outfile);
+
+ do
+ {
+ ret = file_archive_parse_file_iterate(&zlib, &returnerr, path,
+ "", content_zip_file_decompressed, &st);
+ if (!returnerr)
+ break;
+ }while(ret == 0 && !st.found);
+
+ file_archive_parse_file_iterate_stop(&zlib);
+
+ if (st.opt_file)
+ free(st.opt_file);
+ if (st.needle)
+ free(st.needle);
+
+ if (!st.found)
+ return -1;
+
+ return st.size;
+}
+#endif
+
+#endif
+
+#ifdef HAVE_COMPRESSION
+/* Generic compressed file loader.
+ * Extracts to buf, unless optional_filename != 0
+ * Then extracts to optional_filename and leaves buf alone.
+ */
+static int content_file_compressed_read(
+ const char * path, void **buf,
+ const char* optional_filename, ssize_t *length)
+{
+ int ret = 0;
+ const char* file_ext = NULL;
+ struct string_list *str_list = string_split(path, "#");
+
+ /* Safety check.
+ * If optional_filename and optional_filename
+ * exists, we simply return 0,
+ * hoping that optional_filename is the
+ * same as requested.
+ */
+ if (optional_filename && path_file_exists(optional_filename))
+ {
+ *length = 0;
+ return 1;
+ }
+
+ /* We assure that there is something after the '#' symbol.
+ *
+ * This error condition happens for example, when
+ * path = /path/to/file.7z, or
+ * path = /path/to/file.7z#
+ */
+ if (str_list->size <= 1)
+ goto error;
+
+#if defined(HAVE_7ZIP) || defined(HAVE_ZLIB)
+ file_ext = path_get_extension(str_list->elems[0].data);
+#endif
+
+#ifdef HAVE_7ZIP
+ if (string_is_equal_noncase(file_ext, "7z"))
+ {
+ *length = content_7zip_file_read(str_list->elems[0].data,
+ str_list->elems[1].data, buf, optional_filename);
+ if (*length != -1)
+ ret = 1;
+ }
+#endif
+#ifdef HAVE_ZLIB
+ if (string_is_equal_noncase(file_ext, "zip"))
+ {
+ *length = content_zip_file_read(str_list->elems[0].data,
+ str_list->elems[1].data, buf, optional_filename);
+ if (*length != -1)
+ ret = 1;
+ }
+
+ string_list_free(str_list);
+#endif
+ return ret;
+
+error:
+ RARCH_ERR("Could not extract string and substring from "
+ ": %s.\n", path);
+ string_list_free(str_list);
+ *length = 0;
+ return 0;
+}
+#endif
+
+/**
+ * content_file_read:
+ * @path : path to file.
+ * @buf : buffer to allocate and read the contents of the
+ * file into. Needs to be freed manually.
+ * @length : Number of items read, -1 on error.
+ *
+ * Read the contents of a file into @buf. Will call content_file_compressed_read
+ * if path contains a compressed file, otherwise will call filestream_read_file().
+ *
+ * Returns: 1 if file read, 0 on error.
+ */
+static int content_file_read(const char *path, void **buf, ssize_t *length)
+{
+#ifdef HAVE_COMPRESSION
+ if (path_contains_compressed_file(path))
+ {
+ if (content_file_compressed_read(path, buf, NULL, length))
+ return 1;
+ }
+#endif
+ return filestream_read_file(path, buf, length);
+}
+
+struct string_list *compressed_file_list_new(const char *path,
+ const char* ext)
+{
+#if defined(HAVE_ZLIB) || defined(HAVE_7ZIP)
+ const char* file_ext = path_get_extension(path);
+#endif
+
+#ifdef HAVE_7ZIP
+ if (string_is_equal_noncase(file_ext, "7z"))
+ return compressed_7zip_file_list_new(path,ext);
+#endif
+#ifdef HAVE_ZLIB
+ if (string_is_equal_noncase(file_ext, "zip"))
+ return file_archive_get_file_list(path, ext);
+#endif
+ return NULL;
+}
+
+static void check_defaults_dir_create_dir(const char *path)
+{
+ char new_path[PATH_MAX_LENGTH];
+ fill_pathname_expand_special(new_path,
+ path, sizeof(new_path));
+
+ if (path_is_directory(new_path))
+ return;
+ path_mkdir(new_path);
+}
+
+static void check_default_dirs(void)
+{
+ /* early return for people with a custom folder setup
+ so it doesn't create unnecessary directories
+ */
+ if (path_file_exists("custom.ini"))
+ return;
+ if (*g_defaults.dir.core_assets)
+ check_defaults_dir_create_dir(g_defaults.dir.core_assets);
+ if (*g_defaults.dir.remap)
+ check_defaults_dir_create_dir(g_defaults.dir.remap);
+ if (*g_defaults.dir.screenshot)
+ check_defaults_dir_create_dir(g_defaults.dir.screenshot);
+ if (*g_defaults.dir.core)
+ check_defaults_dir_create_dir(g_defaults.dir.core);
+ if (*g_defaults.dir.autoconfig)
+ check_defaults_dir_create_dir(g_defaults.dir.autoconfig);
+ if (*g_defaults.dir.audio_filter)
+ check_defaults_dir_create_dir(g_defaults.dir.audio_filter);
+ if (*g_defaults.dir.video_filter)
+ check_defaults_dir_create_dir(g_defaults.dir.video_filter);
+ if (*g_defaults.dir.assets)
+ check_defaults_dir_create_dir(g_defaults.dir.assets);
+ if (*g_defaults.dir.playlist)
+ check_defaults_dir_create_dir(g_defaults.dir.playlist);
+ if (*g_defaults.dir.core)
+ check_defaults_dir_create_dir(g_defaults.dir.core);
+ if (*g_defaults.dir.core_info)
+ check_defaults_dir_create_dir(g_defaults.dir.core_info);
+ if (*g_defaults.dir.overlay)
+ check_defaults_dir_create_dir(g_defaults.dir.overlay);
+ if (*g_defaults.dir.port)
+ check_defaults_dir_create_dir(g_defaults.dir.port);
+ if (*g_defaults.dir.shader)
+ check_defaults_dir_create_dir(g_defaults.dir.shader);
+ if (*g_defaults.dir.savestate)
+ check_defaults_dir_create_dir(g_defaults.dir.savestate);
+ if (*g_defaults.dir.sram)
+ check_defaults_dir_create_dir(g_defaults.dir.sram);
+ if (*g_defaults.dir.system)
+ check_defaults_dir_create_dir(g_defaults.dir.system);
+ if (*g_defaults.dir.resampler)
+ check_defaults_dir_create_dir(g_defaults.dir.resampler);
+ if (*g_defaults.dir.menu_config)
+ check_defaults_dir_create_dir(g_defaults.dir.menu_config);
+ if (*g_defaults.dir.content_history)
+ check_defaults_dir_create_dir(g_defaults.dir.content_history);
+ if (*g_defaults.dir.cache)
+ check_defaults_dir_create_dir(g_defaults.dir.cache);
+ if (*g_defaults.dir.database)
+ check_defaults_dir_create_dir(g_defaults.dir.database);
+ if (*g_defaults.dir.cursor)
+ check_defaults_dir_create_dir(g_defaults.dir.cursor);
+ if (*g_defaults.dir.cheats)
+ check_defaults_dir_create_dir(g_defaults.dir.cheats);
+ if (*g_defaults.dir.thumbnails)
+ check_defaults_dir_create_dir(g_defaults.dir.thumbnails);
+}
+
+void content_push_to_history_playlist(bool do_push,
+ const char *path, void *data)
+{
+ settings_t *settings = config_get_ptr();
+ struct retro_system_info *info = (struct retro_system_info*)data;
+
+ /* If the history list is not enabled, early return. */
+ if (!settings->history_list_enable)
+ return;
+ if (!g_defaults.history)
+ return;
+ if (!do_push)
+ return;
+
+ playlist_push(g_defaults.history,
+ path,
+ NULL,
+ settings->path.libretro,
+ info->library_name,
+ NULL,
+ NULL);
+}
+
+/**
+ * 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)
+{
+#ifdef HAVE_FILE_LOGGER
+ int i;
+#endif
+
+ *argc = 0;
+ argv[(*argc)++] = strdup("retroarch");
+
+ if (!args->no_content)
+ {
+ if (args->content_path)
+ {
+ RARCH_LOG("Using content: %s.\n", args->content_path);
+ argv[(*argc)++] = strdup(args->content_path);
+ }
+#ifdef HAVE_MENU
+ else
+ {
+ RARCH_LOG("No content, starting dummy core.\n");
+ 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");
+
+#ifdef HAVE_FILE_LOGGER
+ for (i = 0; i < *argc; i++)
+ RARCH_LOG("arg #%d: %s\n", i, argv[i]);
+#endif
+}
+
+/**
+ * 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).
+ **/
+bool content_load(content_ctx_info_t *info)
+{
+ unsigned i;
+ bool retval = true;
+ 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 = (struct rarch_main_wrap*)
+ calloc(1, sizeof(*wrap_args));
+
+ if (!wrap_args)
+ return false;
+
+ retro_assert(wrap_args);
+
+ 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);
+ 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;
+
+ if (!retroarch_main_init(wrap_args->argc, wrap_args->argv))
+ {
+ retval = false;
+ goto error;
+ }
+
+#ifdef HAVE_MENU
+ menu_driver_ctl(RARCH_MENU_CTL_SHADER_MANAGER_INIT, NULL);
+#endif
+ command_event(CMD_EVENT_HISTORY_INIT, NULL);
+ command_event(CMD_EVENT_RESUME, NULL);
+ command_event(CMD_EVENT_VIDEO_SET_ASPECT_RATIO, NULL);
+
+ check_default_dirs();
+
+ frontend_driver_process_args(rarch_argc_ptr, rarch_argv_ptr);
+ frontend_driver_content_loaded();
+
+error:
+ for (i = 0; i < ARRAY_SIZE(argv_copy); i++)
+ free(argv_copy[i]);
+ free(wrap_args);
+ return retval;
+}
+
+/**
+ * read_content_file:
+ * @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 read_content_file(unsigned i, const char *path, void **buf,
+ ssize_t *length)
+{
+#ifdef HAVE_ZLIB
+ content_stream_t stream_info;
+ uint32_t *content_crc_ptr = NULL;
+#endif
+ uint8_t *ret_buf = NULL;
+ global_t *global = global_get_ptr();
+
+ RARCH_LOG("%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)
+ return true;
+
+ /* Attempt to apply a patch. */
+ if (!global->patch.block_patch)
+ patch_content(&ret_buf, length);
+
+#ifdef HAVE_ZLIB
+ content_get_crc(&content_crc_ptr);
+
+ stream_info.a = 0;
+ stream_info.b = ret_buf;
+ stream_info.c = *length;
+
+ if (!stream_backend)
+ stream_backend = file_archive_get_default_file_backend();
+ stream_info.crc = stream_backend->stream_crc_calculate(
+ stream_info.a, stream_info.b, stream_info.c);
+ *content_crc_ptr = stream_info.crc;
+
+ RARCH_LOG("CRC32: 0x%x .\n", (unsigned)*content_crc_ptr);
+#endif
+ *buf = ret_buf;
+
+ return true;
+}
+
+/**
+ * dump_to_file_desperate:
+ * @data : pointer to data buffer.
+ * @size : size of @data.
+ * @type : type of file to be saved.
+ *
+ * Attempt to save valuable RAM data somewhere.
+ **/
+static bool dump_to_file_desperate(const void *data,
+ size_t size, unsigned type)
+{
+ time_t time_;
+ char timebuf[256];
+ char application_data[PATH_MAX_LENGTH];
+ char path[PATH_MAX_LENGTH];
+
+ if (!fill_pathname_application_data(application_data,
+ sizeof(application_data)))
+ return false;
+
+ snprintf(path, sizeof(path), "%s/RetroArch-recovery-%u",
+ application_data, type);
+
+ time(&time_);
+
+ strftime(timebuf, sizeof(timebuf), "%Y-%m-%d-%H-%M-%S", localtime(&time_));
+ strlcat(path, timebuf, sizeof(path));
+
+ if (!filestream_write_file(path, data, size))
+ return false;
+
+ RARCH_WARN("Succeeded in saving RAM data to \"%s\".\n", path);
+ return true;
+}
+
+
+/**
+ * save_state:
+ * @path : path of saved state that shall be written to.
+ *
+ * Save a state from memory to disk.
+ *
+ * Returns: true if successful, false otherwise.
+ **/
+bool content_save_state(const char *path)
+{
+ retro_ctx_serialize_info_t serial_info;
+ retro_ctx_size_info_t info;
+ bool ret = false;
+ void *data = NULL;
+
+ core_serialize_size(&info);
+
+ RARCH_LOG("%s: \"%s\".\n",
+ msg_hash_to_str(MSG_SAVING_STATE),
+ path);
+
+ if (info.size == 0)
+ return false;
+
+ data = malloc(info.size);
+
+ if (!data)
+ return false;
+
+ RARCH_LOG("%s: %d %s.\n",
+ msg_hash_to_str(MSG_STATE_SIZE),
+ (int)info.size,
+ msg_hash_to_str(MSG_BYTES));
+
+ serial_info.data = data;
+ serial_info.size = info.size;
+ ret = core_serialize(&serial_info);
+
+ if (ret)
+ ret = filestream_write_file(path, data, info.size);
+ else
+ {
+ RARCH_ERR("%s \"%s\".\n",
+ msg_hash_to_str(MSG_FAILED_TO_SAVE_STATE_TO),
+ path);
+ }
+
+ free(data);
+
+ return ret;
+}
+
+/**
+ * content_load_state:
+ * @path : path that state will be loaded from.
+ *
+ * Load a state from disk to memory.
+ *
+ * Returns: true if successful, false otherwise.
+ **/
+bool content_load_state(const char *path)
+{
+ unsigned i;
+ ssize_t size;
+ retro_ctx_serialize_info_t serial_info;
+ unsigned num_blocks = 0;
+ void *buf = NULL;
+ struct sram_block *blocks = NULL;
+ settings_t *settings = config_get_ptr();
+ global_t *global = global_get_ptr();
+ bool ret = filestream_read_file(path, &buf, &size);
+
+ RARCH_LOG("%s: \"%s\".\n",
+ msg_hash_to_str(MSG_LOADING_STATE),
+ path);
+
+ if (!ret || size < 0)
+ goto error;
+
+ RARCH_LOG("%s: %u %s.\n",
+ msg_hash_to_str(MSG_STATE_SIZE),
+ (unsigned)size,
+ msg_hash_to_str(MSG_BYTES));
+
+ if (settings->block_sram_overwrite && global->savefiles
+ && global->savefiles->size)
+ {
+ RARCH_LOG("%s.\n",
+ msg_hash_to_str(MSG_BLOCKING_SRAM_OVERWRITE));
+ blocks = (struct sram_block*)
+ calloc(global->savefiles->size, sizeof(*blocks));
+
+ if (blocks)
+ {
+ num_blocks = global->savefiles->size;
+ for (i = 0; i < num_blocks; i++)
+ blocks[i].type = global->savefiles->elems[i].attr.i;
+ }
+ }
+
+
+ for (i = 0; i < num_blocks; i++)
+ {
+ retro_ctx_memory_info_t mem_info;
+
+ mem_info.id = blocks[i].type;
+ core_get_memory(&mem_info);
+
+ blocks[i].size = mem_info.size;
+ }
+
+ for (i = 0; i < num_blocks; i++)
+ if (blocks[i].size)
+ blocks[i].data = malloc(blocks[i].size);
+
+ /* Backup current SRAM which is overwritten by unserialize. */
+ for (i = 0; i < num_blocks; i++)
+ {
+ if (blocks[i].data)
+ {
+ retro_ctx_memory_info_t mem_info;
+ const void *ptr = NULL;
+
+ mem_info.id = blocks[i].type;
+
+ core_get_memory(&mem_info);
+
+ ptr = mem_info.data;
+ if (ptr)
+ memcpy(blocks[i].data, ptr, blocks[i].size);
+ }
+ }
+
+ serial_info.data_const = buf;
+ serial_info.size = size;
+ ret = core_unserialize(&serial_info);
+
+ /* Flush back. */
+ for (i = 0; i < num_blocks; i++)
+ {
+ if (blocks[i].data)
+ {
+ retro_ctx_memory_info_t mem_info;
+ void *ptr = NULL;
+
+ mem_info.id = blocks[i].type;
+
+ core_get_memory(&mem_info);
+
+ ptr = mem_info.data;
+ if (ptr)
+ memcpy(ptr, blocks[i].data, blocks[i].size);
+ }
+ }
+
+ for (i = 0; i < num_blocks; i++)
+ free(blocks[i].data);
+ free(blocks);
+ free(buf);
+
+ if (!ret)
+ goto error;
+
+ return true;
+
+error:
+ RARCH_ERR("%s \"%s\".\n",
+ msg_hash_to_str(MSG_FAILED_TO_LOAD_STATE),
+ path);
+ return false;
+}
+
+/**
+ * content_load_ram_file:
+ * @path : path of RAM state that will be loaded from.
+ * @type : type of memory
+ *
+ * Load a RAM state from disk to memory.
+ */
+bool content_load_ram_file(ram_type_t *ram)
+{
+ ssize_t rc;
+ retro_ctx_memory_info_t mem_info;
+ void *buf = NULL;
+
+ if (!ram)
+ return false;
+
+ mem_info.id = ram->type;
+
+ core_get_memory(&mem_info);
+
+ if (mem_info.size == 0 || !mem_info.data)
+ return false;
+
+ if (!filestream_read_file(ram->path, &buf, &rc))
+ return false;
+
+ if (rc > 0)
+ {
+ if (rc > (ssize_t)mem_info.size)
+ {
+ RARCH_WARN("SRAM is larger than implementation expects, "
+ "doing partial load (truncating %u %s %s %u).\n",
+ (unsigned)rc,
+ msg_hash_to_str(MSG_BYTES),
+ msg_hash_to_str(MSG_TO),
+ (unsigned)mem_info.size);
+ rc = mem_info.size;
+ }
+ memcpy(mem_info.data, buf, rc);
+ }
+
+ if (buf)
+ free(buf);
+
+ return true;
+}
+
+/**
+ * content_save_ram_file:
+ * @path : path of RAM state that shall be written to.
+ * @type : type of memory
+ *
+ * Save a RAM state from memory to disk.
+ *
+ */
+bool content_save_ram_file(ram_type_t *ram)
+{
+ retro_ctx_memory_info_t mem_info;
+
+ if (!ram)
+ return false;
+
+ mem_info.id = ram->type;
+
+ core_get_memory(&mem_info);
+
+ if (!mem_info.data || mem_info.size == 0)
+ return false;
+
+ if (!filestream_write_file(ram->path, mem_info.data, mem_info.size))
+ {
+ RARCH_ERR("%s.\n",
+ msg_hash_to_str(MSG_FAILED_TO_SAVE_SRAM));
+ RARCH_WARN("Attempting to recover ...\n");
+
+ /* In case the file could not be written to,
+ * the fallback function 'dump_to_file_desperate'
+ * will be called. */
+ if (!dump_to_file_desperate(mem_info.data, mem_info.size, ram->type))
+ {
+ RARCH_WARN("Failed ... Cannot recover save file.\n");
+ }
+ return false;
+ }
+
+ RARCH_LOG("%s \"%s\".\n",
+ msg_hash_to_str(MSG_SAVED_SUCCESSFULLY_TO),
+ ram->path);
+
+ return true;
+}
+
+/* Load the content into memory. */
+static bool load_content_into_memory(
+ struct retro_game_info *info,
+ unsigned i,
+ const char *path)
+{
+ ssize_t len;
+ bool ret = false;
+
+ if (i == 0)
+ {
+ /* First content file is significant, attempt to do patching,
+ * CRC checking, etc. */
+ ret = read_content_file(i, path, (void**)&info->data, &len);
+ }
+ else
+ ret = content_file_read(path, (void**)&info->data, &len);
+
+ if (!ret || len < 0)
+ goto error;
+
+ info->size = len;
+
+ return true;
+
+error:
+ RARCH_ERR("%s \"%s\".\n",
+ msg_hash_to_str(MSG_COULD_NOT_READ_CONTENT_FILE),
+ path);
+ return false;
+}
+
+#ifdef HAVE_COMPRESSION
+static bool load_content_from_compressed_archive(
+ struct string_list *temporary_content,
+ struct retro_game_info *info, unsigned i,
+ struct string_list* additional_path_allocs,
+ bool need_fullpath, const char *path)
+{
+ union string_list_elem_attr attributes;
+ char new_path[PATH_MAX_LENGTH];
+ char new_basedir[PATH_MAX_LENGTH];
+ ssize_t new_path_len = 0;
+ bool ret = false;
+ settings_t *settings = config_get_ptr();
+ rarch_system_info_t *sys_info= NULL;
+
+ runloop_ctl(RUNLOOP_CTL_SYSTEM_INFO_GET, &sys_info);
+
+ if (sys_info && sys_info->info.block_extract)
+ return true;
+ if (!need_fullpath || !path_contains_compressed_file(path))
+ return true;
+
+ RARCH_LOG("Compressed file in case of need_fullpath."
+ " Now extracting to temporary directory.\n");
+
+ strlcpy(new_basedir, settings->directory.cache,
+ sizeof(new_basedir));
+
+ if (string_is_empty(new_basedir) || !path_is_directory(new_basedir))
+ {
+ RARCH_WARN("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));
+ }
+
+ attributes.i = 0;
+ fill_pathname_join(new_path, new_basedir,
+ path_basename(path), sizeof(new_path));
+
+ ret = content_file_compressed_read(path, NULL, new_path, &new_path_len);
+
+ if (!ret || new_path_len < 0)
+ {
+ RARCH_ERR("%s \"%s\".\n",
+ msg_hash_to_str(MSG_COULD_NOT_READ_CONTENT_FILE),
+ path);
+ return false;
+ }
+
+ RARCH_LOG("New path is: [%s]\n", new_path);
+
+ string_list_append(additional_path_allocs, new_path, attributes);
+ info[i].path =
+ additional_path_allocs->elems[additional_path_allocs->size -1 ].data;
+
+ if (!string_list_append(temporary_content, new_path, attributes))
+ return false;
+
+ return true;
+}
+#endif
+
+/**
+ * load_content:
+ * @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 load_content(
+ struct string_list *temporary_content,
+ struct retro_game_info *info,
+ const struct string_list *content,
+ const struct retro_subsystem_info *special,
+ struct string_list* additional_path_allocs
+ )
+{
+ unsigned i;
+ retro_ctx_load_content_info_t load_info;
+
+ if (!info || !additional_path_allocs)
+ return false;
+
+ for (i = 0; i < content->size; i++)
+ {
+ int attr = content->elems[i].attr.i;
+ bool need_fullpath = attr & 2;
+ bool require_content = attr & 4;
+ const char *path = content->elems[i].data;
+
+ if (require_content && string_is_empty(path))
+ {
+ RARCH_LOG("libretro core requires content, "
+ "but nothing was provided.\n");
+ return false;
+ }
+
+ info[i].path = NULL;
+
+ if (*path)
+ info[i].path = path;
+
+ if (!need_fullpath && !string_is_empty(path))
+ {
+ if (!load_content_into_memory(&info[i], i, path))
+ return false;
+ }
+ else
+ {
+ RARCH_LOG("Content loading skipped. Implementation will"
+ " load it on its own.\n");
+
+#ifdef HAVE_COMPRESSION
+ if (!load_content_from_compressed_archive(
+ temporary_content,
+ &info[i], i,
+ additional_path_allocs, need_fullpath, path))
+ return false;
+#endif
+ }
+ }
+
+ load_info.content = content;
+ load_info.special = special;
+ load_info.info = info;
+
+ if (!core_load_game(&load_info))
+ {
+ RARCH_ERR("%s.\n", msg_hash_to_str(MSG_FAILED_TO_LOAD_CONTENT));
+ return false;
+ }
+
+#ifdef HAVE_CHEEVOS
+ if (!special)
+ {
+ const void *load_data = NULL;
+
+ cheevos_set_cheats();
+
+ if (*content->elems[0].data)
+ load_data = info;
+ cheevos_load(load_data);
+ }
+#endif
+
+ return true;
+}
+
+static const struct retro_subsystem_info *init_content_file_subsystem(
+ bool *ret, rarch_system_info_t *system)
+{
+ global_t *global = global_get_ptr();
+ const struct retro_subsystem_info *special =
+ libretro_find_subsystem_info(system->subsystem.data,
+ system->subsystem.size, global->subsystem);
+
+ if (!special)
+ {
+ RARCH_ERR(
+ "Failed to find subsystem \"%s\" in libretro implementation.\n",
+ global->subsystem);
+ goto error;
+ }
+
+ if (special->num_roms && !global->subsystem_fullpaths)
+ {
+ RARCH_ERR("libretro core requires special content, "
+ "but none were provided.\n");
+ goto error;
+ }
+ else if (special->num_roms && special->num_roms
+ != global->subsystem_fullpaths->size)
+ {
+ RARCH_ERR("libretro core requires %u content files for "
+ "subsystem \"%s\", but %u content files were provided.\n",
+ special->num_roms, special->desc,
+ (unsigned)global->subsystem_fullpaths->size);
+ goto error;
+ }
+ else if (!special->num_roms && global->subsystem_fullpaths
+ && global->subsystem_fullpaths->size)
+ {
+ RARCH_ERR("libretro core takes no content for subsystem \"%s\", "
+ "but %u content files were provided.\n",
+ special->desc,
+ (unsigned)global->subsystem_fullpaths->size);
+ goto error;
+ }
+
+ *ret = true;
+ return special;
+
+error:
+ *ret = false;
+ return NULL;
+}
+
+#ifdef HAVE_ZLIB
+static bool init_content_file_extract(
+ struct string_list *temporary_content,
+ struct string_list *content,
+ rarch_system_info_t *system,
+ const struct retro_subsystem_info *special,
+ union string_list_elem_attr *attr
+ )
+{
+ unsigned i;
+ settings_t *settings = config_get_ptr();
+
+ for (i = 0; i < content->size; i++)
+ {
+ const char *ext = NULL;
+ const char *valid_ext = system->info.valid_extensions;
+
+ /* Block extract check. */
+ if (content->elems[i].attr.i & 1)
+ continue;
+
+ ext = path_get_extension(content->elems[i].data);
+
+ if (special)
+ valid_ext = special->roms[i].valid_extensions;
+
+ if (!ext)
+ continue;
+
+ if (string_is_equal_noncase(ext, "zip"))
+ {
+ char new_path[PATH_MAX_LENGTH];
+ char temp_content[PATH_MAX_LENGTH];
+
+ strlcpy(temp_content, content->elems[i].data,
+ sizeof(temp_content));
+
+ if (!file_archive_extract_first_content_file(temp_content,
+ sizeof(temp_content), valid_ext,
+ *settings->directory.cache ?
+ settings->directory.cache : NULL,
+ new_path, sizeof(new_path)))
+ {
+ RARCH_ERR("Failed to extract content from zipped file: %s.\n",
+ temp_content);
+ return false;
+ }
+
+ string_list_set(content, i, new_path);
+ if (!string_list_append(temporary_content,
+ new_path, *attr))
+ return false;
+ }
+ }
+
+ return true;
+}
+#endif
+
+static bool init_content_file_set_attribs(
+ struct string_list *temporary_content,
+ struct string_list *content,
+ rarch_system_info_t *system,
+ const struct retro_subsystem_info *special)
+{
+ union string_list_elem_attr attr;
+ global_t *global = global_get_ptr();
+
+ attr.i = 0;
+
+ if (*global->subsystem)
+ {
+ unsigned i;
+
+ for (i = 0; i < global->subsystem_fullpaths->size; i++)
+ {
+ attr.i = special->roms[i].block_extract;
+ attr.i |= special->roms[i].need_fullpath << 1;
+ attr.i |= special->roms[i].required << 2;
+
+ string_list_append(content,
+ global->subsystem_fullpaths->elems[i].data, attr);
+ }
+ }
+ else
+ {
+ settings_t *settings = config_get_ptr();
+
+ attr.i = system->info.block_extract;
+ attr.i |= system->info.need_fullpath << 1;
+ attr.i |= (!content_does_not_need_content()) << 2;
+
+ if (content_does_not_need_content()
+ && settings->set_supports_no_game_enable)
+ string_list_append(content, "", attr);
+ else
+ {
+ char *fullpath = NULL;
+ runloop_ctl(RUNLOOP_CTL_GET_CONTENT_PATH, &fullpath);
+
+ string_list_append(content, fullpath, attr);
+ }
+ }
+
+#ifdef HAVE_ZLIB
+ /* Try to extract all content we're going to load if appropriate. */
+ if (!init_content_file_extract(temporary_content,
+ content, system, special, &attr))
+ return false;
+#endif
+ return true;
+}
+
+/**
+ * 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(struct string_list *temporary_content)
+{
+ unsigned i;
+ struct retro_game_info *info = NULL;
+ bool ret = false;
+ struct string_list* additional_path_allocs = NULL;
+ struct string_list *content = NULL;
+ const struct retro_subsystem_info *special = NULL;
+ rarch_system_info_t *system = NULL;
+ global_t *global = global_get_ptr();
+
+ runloop_ctl(RUNLOOP_CTL_SYSTEM_INFO_GET, &system);
+
+ if (*global->subsystem)
+ {
+ special = init_content_file_subsystem(&ret, system);
+ if (!ret)
+ goto error;
+ }
+
+ content = string_list_new();
+
+ if (!content)
+ goto error;
+
+ if (!init_content_file_set_attribs(temporary_content,
+ content, system, special))
+ goto error;
+
+ info = (struct retro_game_info*)
+ calloc(content->size, sizeof(*info));
+ additional_path_allocs = string_list_new();
+
+ ret = load_content(temporary_content,
+ info, content, special, additional_path_allocs);
+
+ for (i = 0; i < content->size; i++)
+ free((void*)info[i].data);
+
+ string_list_free(additional_path_allocs);
+ if (info)
+ free(info);
+
+error:
+ if (content)
+ string_list_free(content);
+ return ret;
+}
+
+static bool content_file_free(struct string_list *temporary_content)
+{
+ unsigned i;
+
+ if (!temporary_content)
+ return false;
+
+ for (i = 0; i < temporary_content->size; i++)
+ {
+ const char *path = temporary_content->elems[i].data;
+
+ RARCH_LOG("%s: %s.\n",
+ msg_hash_to_str(MSG_REMOVING_TEMPORARY_CONTENT_FILE), path);
+ if (remove(path) < 0)
+ RARCH_ERR("%s: %s.\n",
+ msg_hash_to_str(MSG_FAILED_TO_REMOVE_TEMPORARY_FILE),
+ path);
+ }
+ string_list_free(temporary_content);
+
+ return true;
+}
+
+bool content_does_not_need_content(void)
+{
+ return core_does_not_need_content;
+}
+
+void content_set_does_not_need_content(void)
+{
+ core_does_not_need_content = true;
+}
+
+void content_unset_does_not_need_content(void)
+{
+ core_does_not_need_content = false;
+}
+
+bool content_get_crc(uint32_t **content_crc_ptr)
+{
+ if (!content_crc_ptr)
+ return false;
+ *content_crc_ptr = &content_crc;
+ return true;
+}
+
+bool content_is_inited(void)
+{
+ return _content_is_inited;
+}
+
+void content_deinit(void)
+{
+ content_file_free(temporary_content);
+ temporary_content = NULL;
+ content_crc = 0;
+ _content_is_inited = false;
+ core_does_not_need_content = false;
+}
+
+/* Initializes and loads a content file for the currently
+ * selected libretro core. */
+bool content_init(void)
+{
+ temporary_content = string_list_new();
+ if (!temporary_content)
+ goto error;
+
+ if (!content_file_init(temporary_content))
+ goto error;
+
+ _content_is_inited = true;
+ return true;
+
+error:
+ content_deinit();
+ return false;
+}
+
/* TODO/FIXME - turn this into actual task */
#ifdef HAVE_MENU