create 7z archive backend, enables scanning of 7z content

This commit is contained in:
Brad Parker 2016-09-18 00:38:59 -04:00
parent 0c48555ba2
commit cb885f9c20
22 changed files with 1226 additions and 1000 deletions

View File

@ -989,14 +989,13 @@ ifeq ($(HAVE_7ZIP),1)
deps/7zip/7zCrc.o \
deps/7zip/Lzma2Dec.o \
deps/7zip/7zBuf.o
OBJ += $(7ZOBJ)
OBJ += libretro-common/file/archive_file_7z.o \
$(7ZOBJ)
endif
ifeq ($(HAVE_ZLIB), 1)
OBJ += libretro-common/file/archive_file.o \
libretro-common/file/archive_file_zlib.o \
tasks/task_decompress.o
OBJ += libretro-common/file/archive_file_zlib.o
OBJ += $(ZLIB_OBJS)
DEFINES += -DHAVE_ZLIB
HAVE_COMPRESSION = 1
@ -1147,6 +1146,8 @@ endif
ifeq ($(HAVE_COMPRESSION), 1)
DEFINES += -DHAVE_COMPRESSION
OBJ += libretro-common/file/archive_file.o \
tasks/task_decompress.o
endif
#ifeq ($(HAVE_DIRECTX), 1)

View File

@ -46,6 +46,7 @@ else
CFLAGS += -DHAVE_COMPRESSION
OBJS += libretro-common/file/archive_file.o
OBJS += libretro-common/file/archive_file_zlib.o
OBJS += libretro-common/file/archive_file_7z.o
OBJS += libretro-common/encodings/encoding_utf.o
OBJS += verbosity.o
OBJS += performance.o

View File

@ -872,7 +872,7 @@ static char buildbot_assets_server_url[] = "http://buildbot.libretro.com/assets/
/* User 1 */
static const struct retro_keybind retro_keybinds_1[] = {
/* | RetroPad button | desc | keyboard key | js btn | js axis | */
/* | RetroPad button | desc | keyboard key | js btn | js axis | */
{ true, RETRO_DEVICE_ID_JOYPAD_B, RETRO_LBL_JOYPAD_B, RETROK_z, NO_BTN, 0, AXIS_NONE },
{ true, RETRO_DEVICE_ID_JOYPAD_Y, RETRO_LBL_JOYPAD_Y, RETROK_a, NO_BTN, 0, AXIS_NONE },
{ true, RETRO_DEVICE_ID_JOYPAD_SELECT, RETRO_LBL_JOYPAD_SELECT, RETROK_RSHIFT, NO_BTN, 0, AXIS_NONE },
@ -936,7 +936,7 @@ static const struct retro_keybind retro_keybinds_1[] = {
/* Users 2 to MAX_USERS */
static const struct retro_keybind retro_keybinds_rest[] = {
/* | RetroPad button | desc | keyboard key | js btn | js axis | */
/* | RetroPad button | desc | keyboard key | js btn | js axis | */
{ true, RETRO_DEVICE_ID_JOYPAD_B, RETRO_LBL_JOYPAD_B, RETROK_UNKNOWN, NO_BTN, 0, AXIS_NONE },
{ true, RETRO_DEVICE_ID_JOYPAD_Y, RETRO_LBL_JOYPAD_Y, RETROK_UNKNOWN, NO_BTN, 0, AXIS_NONE },
{ true, RETRO_DEVICE_ID_JOYPAD_SELECT, RETRO_LBL_JOYPAD_SELECT, RETROK_UNKNOWN, NO_BTN, 0, AXIS_NONE },

View File

@ -2,7 +2,7 @@
* Copyright (C) 2010-2014 - Hans-Kristian Arntzen
* Copyright (C) 2011-2016 - Daniel De Matteis
* Copyright (C) 2013-2015 - Jason Fetters
*
*
* 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.
@ -46,7 +46,7 @@ enum database_type
{
DATABASE_TYPE_NONE = 0,
DATABASE_TYPE_ITERATE,
DATABASE_TYPE_ITERATE_ZIP,
DATABASE_TYPE_ITERATE_ARCHIVE,
DATABASE_TYPE_ITERATE_LUTRO,
DATABASE_TYPE_SERIAL_LOOKUP,
DATABASE_TYPE_CRC_LOOKUP
@ -58,7 +58,7 @@ typedef struct
enum database_type type;
size_t list_ptr;
struct string_list *list;
#ifdef HAVE_ZLIB
#ifdef HAVE_COMPRESSION
file_archive_transfer_t state;
#endif
} database_info_handle_t;

View File

@ -21,6 +21,7 @@
*/
#include <stdint.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
@ -40,7 +41,7 @@ static INLINE unsigned leading_ones(uint8_t c)
return ones;
}
/* Simple implementation. Assumes the sequence is
/* Simple implementation. Assumes the sequence is
* properly synchronized and terminated. */
size_t utf8_conv_utf32(uint32_t *out, size_t out_chars,
@ -119,14 +120,14 @@ bool utf16_conv_utf8(uint8_t *out, size_t *out_chars,
if (value < (((uint32_t)1) << (numAdds * 5 + 6)))
break;
if (out)
out[out_pos] = (char)(kUtf8Limits[numAdds - 1]
out[out_pos] = (char)(kUtf8Limits[numAdds - 1]
+ (value >> (6 * numAdds)));
out_pos++;
do
{
numAdds--;
if (out)
out[out_pos] = (char)(0x80
out[out_pos] = (char)(0x80
+ ((value >> (6 * numAdds)) & 0x3F));
out_pos++;
}while (numAdds != 0);
@ -136,13 +137,13 @@ bool utf16_conv_utf8(uint8_t *out, size_t *out_chars,
return false;
}
/* Acts mostly like strlcpy.
/* Acts mostly like strlcpy.
*
* Copies the given number of UTF-8 characters,
* Copies the given number of UTF-8 characters,
* but at most d_len bytes.
*
* Always NULL terminates.
* Does not copy half a character.
* Always NULL terminates.
* Does not copy half a character.
*
* Returns number of bytes. 's' is assumed valid UTF-8.
* Use only if 'chars' is considerably less than 'd_len'. */
@ -205,20 +206,55 @@ uint32_t utf8_walk(const char **string)
{
uint8_t first = utf8_walkbyte(string);
uint32_t ret;
if (first<128)
return first;
ret = 0;
ret = (ret<<6) | (utf8_walkbyte(string) & 0x3F);
if (first >= 0xE0)
ret = (ret<<6) | (utf8_walkbyte(string) & 0x3F);
if (first >= 0xF0)
ret = (ret<<6) | (utf8_walkbyte(string) & 0x3F);
if (first >= 0xF0)
return ret | (first&31)<<18;
if (first >= 0xE0)
return ret | (first&15)<<12;
return ret | (first&7)<<6;
}
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);
}
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;
}

View File

@ -45,30 +45,7 @@
#include <retro_stat.h>
#include <retro_miscellaneous.h>
#include <lists/string_list.h>
#ifndef CENTRAL_FILE_HEADER_SIGNATURE
#define CENTRAL_FILE_HEADER_SIGNATURE 0x02014b50
#endif
#ifndef END_OF_CENTRAL_DIR_SIGNATURE
#define END_OF_CENTRAL_DIR_SIGNATURE 0x06054b50
#endif
struct zip_extract_userdata
{
char *zip_path;
char *first_extracted_file_path;
const char *extraction_directory;
size_t zip_path_size;
struct string_list *ext;
bool found_content;
};
enum file_archive_compression_mode
{
ZLIB_MODE_UNCOMPRESSED = 0,
ZLIB_MODE_DEFLATE = 8
};
#include <string/stdstring.h>
typedef struct
{
@ -81,7 +58,7 @@ typedef struct
#ifdef HAVE_MMAP
/* Closes, unmaps and frees. */
static void file_archive_free(void *handle)
void file_archive_free(void *handle)
{
file_archive_file_data_t *data = (file_archive_file_data_t*)handle;
@ -95,7 +72,7 @@ static void file_archive_free(void *handle)
free(data);
}
static const uint8_t *file_archive_data(void *handle)
const uint8_t *file_archive_data(void *handle)
{
file_archive_file_data_t *data = (file_archive_file_data_t*)handle;
if (!data)
@ -146,16 +123,17 @@ error:
#else
/* Closes, unmaps and frees. */
static void file_archive_free(void *handle)
void file_archive_free(void *handle)
{
file_archive_file_data_t *data = (file_archive_file_data_t*)handle;
if (!data)
return;
free(data->data);
if(data->data)
free(data->data);
free(data);
}
static const uint8_t *file_archive_data(void *handle)
const uint8_t *file_archive_data(void *handle)
{
file_archive_file_data_t *data = (file_archive_file_data_t*)handle;
if (!data)
@ -209,7 +187,9 @@ static int file_archive_get_file_list_cb(
union string_list_elem_attr attr;
struct string_list *ext_list = NULL;
const char *file_ext = NULL;
struct string_list *list = (struct string_list*)userdata;
struct archive_extract_userdata *data =
(struct archive_extract_userdata*)userdata;
size_t pathLen = strlen(path);
(void)cdata;
(void)cmode;
@ -219,13 +199,16 @@ static int file_archive_get_file_list_cb(
memset(&attr, 0, sizeof(attr));
if (!pathLen)
return 0;
if (valid_exts)
ext_list = string_split(valid_exts, "|");
if (ext_list)
{
/* Checks if this entry is a directory or a file. */
char last_char = path[strlen(path)-1];
char last_char = path[pathLen-1];
/* Skip if directory. */
if (last_char == '/' || last_char == '\\' )
@ -233,7 +216,7 @@ static int file_archive_get_file_list_cb(
file_ext = path_get_extension(path);
if (!file_ext ||
if (!file_ext ||
!string_list_find_elem_prefix(ext_list, ".", file_ext))
goto error;
@ -241,8 +224,8 @@ static int file_archive_get_file_list_cb(
string_list_free(ext_list);
}
return string_list_append(list, path, attr);
return string_list_append(data->list, path, attr);
error:
string_list_free(ext_list);
return 0;
@ -254,9 +237,9 @@ static int file_archive_extract_cb(const char *name, const char *valid_exts,
uint32_t checksum, void *userdata)
{
const char *ext = path_get_extension(name);
struct zip_extract_userdata *data = (struct zip_extract_userdata*)userdata;
struct archive_extract_userdata *data = (struct archive_extract_userdata*)userdata;
/* Extract first content that matches our list. */
/* Extract first file that matches our list. */
if (ext && string_list_find_elem(data->ext, ext))
{
char new_path[PATH_MAX_LENGTH] = {0};
@ -265,141 +248,51 @@ static int file_archive_extract_cb(const char *name, const char *valid_exts,
fill_pathname_join(new_path, data->extraction_directory,
path_basename(name), sizeof(new_path));
else
fill_pathname_resolve_relative(new_path, data->zip_path,
fill_pathname_resolve_relative(new_path, data->archive_path,
path_basename(name), sizeof(new_path));
data->first_extracted_file_path = strdup(new_path);
data->found_content = file_archive_perform_mode(new_path,
data->found_file = file_archive_perform_mode(new_path,
valid_exts, cdata, cmode, csize, size,
0, NULL);
0, data);
return 0;
}
return 1;
}
static uint32_t read_le(const uint8_t *data, unsigned size)
{
unsigned i;
uint32_t val = 0;
size *= 8;
for (i = 0; i < size; i += 8)
val |= (uint32_t)*data++ << i;
return val;
}
static int file_archive_parse_file_iterate_step_internal(
file_archive_transfer_t *state, char *filename,
const uint8_t **cdata,
unsigned *cmode, uint32_t *size, uint32_t *csize,
uint32_t *checksum, unsigned *payback)
{
uint32_t offset;
uint32_t namelength, extralength, commentlength,
offsetNL, offsetEL;
uint32_t signature = read_le(state->directory + 0, 4);
if (signature != CENTRAL_FILE_HEADER_SIGNATURE)
return 0;
*cmode = read_le(state->directory + 10, 2);
*checksum = read_le(state->directory + 16, 4);
*csize = read_le(state->directory + 20, 4);
*size = read_le(state->directory + 24, 4);
namelength = read_le(state->directory + 28, 2);
extralength = read_le(state->directory + 30, 2);
commentlength = read_le(state->directory + 32, 2);
if (namelength >= PATH_MAX_LENGTH)
return -1;
memcpy(filename, state->directory + 46, namelength);
offset = read_le(state->directory + 42, 4);
offsetNL = read_le(state->data + offset + 26, 2);
offsetEL = read_le(state->data + offset + 28, 2);
*cdata = state->data + offset + 30 + offsetNL + offsetEL;
*payback = 46 + namelength + extralength + commentlength;
return 1;
}
static int file_archive_parse_file_iterate_step(file_archive_transfer_t *state,
const char *valid_exts, void *userdata, file_archive_file_cb file_cb)
{
const uint8_t *cdata = NULL;
uint32_t checksum = 0;
uint32_t size = 0;
uint32_t csize = 0;
unsigned cmode = 0;
unsigned payload = 0;
char filename[PATH_MAX_LENGTH] = {0};
int ret = file_archive_parse_file_iterate_step_internal(state, filename,
&cdata, &cmode, &size, &csize,
&checksum, &payload);
if (ret != 1)
return ret;
#if 0
RARCH_LOG("OFFSET: %u, CSIZE: %u, SIZE: %u.\n", offset + 30 +
offsetNL + offsetEL, csize, size);
#endif
if (!file_cb(filename, valid_exts, cdata, cmode,
csize, size, checksum, userdata))
return 0;
state->directory += payload;
return 1;
}
static int file_archive_parse_file_init(file_archive_transfer_t *state,
int file_archive_parse_file_init(file_archive_transfer_t *state,
const char *file)
{
state->backend = file_archive_get_default_file_backend();
char path[PATH_MAX_LENGTH] = {0};
strlcpy(path, file, sizeof(path));
char *last = (char*)path_get_archive_delim(path);
if (last)
*last = '\0';
state->backend = file_archive_get_file_backend(path);
if (!state->backend)
return -1;
state->handle = file_archive_open(file);
state->handle = file_archive_open(path);
if (!state->handle)
return -1;
state->zip_size = file_archive_size(state->handle);
if (state->zip_size < 22)
return -1;
state->archive_size = file_archive_size(state->handle);
state->data = file_archive_data(state->handle);
state->footer = state->data + state->zip_size - 22;
state->footer = 0;
state->directory = 0;
for (;; state->footer--)
{
if (state->footer <= state->data + 22)
return -1;
if (read_le(state->footer, 4) == END_OF_CENTRAL_DIR_SIGNATURE)
{
unsigned comment_len = read_le(state->footer + 20, 2);
if (state->footer + 22 + comment_len == state->data + state->zip_size)
break;
}
}
state->directory = state->data + read_le(state->footer + 16, 4);
return 0;
return state->backend->archive_parse_file_init(state, path);
}
/**
* file_archive_decompress_data_to_file:
* @path : filename path of archive.
* @valid_exts : Valid extensions of archive to be parsed.
* @valid_exts : Valid extensions of archive to be parsed.
* If NULL, allow all.
* @cdata : input data.
* @csize : size of input data.
@ -420,12 +313,6 @@ static int file_archive_decompress_data_to_file(
uint32_t size,
uint32_t checksum)
{
if (handle)
{
handle->backend->stream_free(handle->stream);
free(handle->stream);
}
if (!handle || ret == -1)
{
ret = 0;
@ -434,12 +321,13 @@ static int file_archive_decompress_data_to_file(
handle->real_checksum = handle->backend->stream_crc_calculate(
0, handle->data, size);
handle->backend->stream_free(handle->stream);
#if 0
if (handle->real_checksum != checksum)
{
/* File CRC difers from ZIP CRC. */
printf("File CRC differs from ZIP CRC. File: 0x%x, ZIP: 0x%x.\n",
/* File CRC difers from archive CRC. */
printf("File CRC differs from archive CRC. File: 0x%x, Archive: 0x%x.\n",
(unsigned)handle->real_checksum, (unsigned)checksum);
}
#endif
@ -456,6 +344,15 @@ end:
return ret;
}
void file_archive_parse_file_iterate_stop(file_archive_transfer_t *state)
{
if (!state || !state->handle)
return;
state->type = ARCHIVE_TRANSFER_DEINIT;
file_archive_parse_file_iterate(state, NULL, NULL, NULL, NULL, NULL);
}
int file_archive_parse_file_iterate(
file_archive_transfer_t *state,
bool *returnerr,
@ -469,58 +366,81 @@ int file_archive_parse_file_iterate(
switch (state->type)
{
case ZLIB_TRANSFER_NONE:
case ARCHIVE_TRANSFER_NONE:
break;
case ZLIB_TRANSFER_INIT:
case ARCHIVE_TRANSFER_INIT:
if (file_archive_parse_file_init(state, file) == 0)
state->type = ZLIB_TRANSFER_ITERATE;
else
state->type = ZLIB_TRANSFER_DEINIT_ERROR;
break;
case ZLIB_TRANSFER_ITERATE:
{
int ret = file_archive_parse_file_iterate_step(state,
valid_exts, userdata, file_cb);
if (ret != 1)
state->type = ZLIB_TRANSFER_DEINIT;
if (ret == -1)
state->type = ZLIB_TRANSFER_DEINIT_ERROR;
struct archive_extract_userdata *data =
(struct archive_extract_userdata*)userdata;
if (data)
data->context = state->stream;
state->type = ARCHIVE_TRANSFER_ITERATE;
}
else
state->type = ARCHIVE_TRANSFER_DEINIT_ERROR;
break;
case ARCHIVE_TRANSFER_ITERATE:
{
const struct file_archive_file_backend *backend = file_archive_get_file_backend(file);
if (backend)
{
int ret = backend->archive_parse_file_iterate_step(state,
valid_exts, userdata, file_cb);
if (ret != 1)
state->type = ARCHIVE_TRANSFER_DEINIT;
if (ret == -1)
state->type = ARCHIVE_TRANSFER_DEINIT_ERROR;
// early return to prevent deinit from never firing
return 0;
}
else
return -1;
}
break;
case ZLIB_TRANSFER_DEINIT_ERROR:
case ARCHIVE_TRANSFER_DEINIT_ERROR:
*returnerr = false;
case ZLIB_TRANSFER_DEINIT:
case ARCHIVE_TRANSFER_DEINIT:
if (state->handle)
{
file_archive_free(state->handle);
state->handle = NULL;
state->handle = NULL;
}
if (state->stream && state->backend)
{
struct archive_extract_userdata *data =
(struct archive_extract_userdata*)userdata;
state->backend->stream_free(state->stream);
if (state->stream)
free(state->stream);
state->stream = NULL;
if (data)
data->context = NULL;
}
break;
}
if (state->type == ZLIB_TRANSFER_DEINIT ||
state->type == ZLIB_TRANSFER_DEINIT_ERROR)
if (state->type == ARCHIVE_TRANSFER_DEINIT ||
state->type == ARCHIVE_TRANSFER_DEINIT_ERROR)
return -1;
return 0;
}
void file_archive_parse_file_iterate_stop(file_archive_transfer_t *state)
{
if (!state || !state->handle)
return;
state->type = ZLIB_TRANSFER_DEINIT;
file_archive_parse_file_iterate(state, NULL, NULL, NULL, NULL, NULL);
}
/**
* file_archive_parse_file:
* @file : filename path of archive
* @valid_exts : Valid extensions of archive to be parsed.
* @valid_exts : Valid extensions of archive to be parsed.
* If NULL, allow all.
* @file_cb : file_cb function pointer
* @userdata : userdata to pass to file_cb function pointer.
*
* Low-level file parsing. Enumerates over all files and calls
* Low-level file parsing. Enumerates over all files and calls
* file_cb with userdata.
*
* Returns: true (1) on success, otherwise false (0).
@ -531,7 +451,7 @@ static bool file_archive_parse_file(const char *file, const char *valid_exts,
file_archive_transfer_t state = {0};
bool returnerr = true;
state.type = ZLIB_TRANSFER_INIT;
state.type = ARCHIVE_TRANSFER_INIT;
for (;;)
{
@ -547,33 +467,33 @@ int file_archive_parse_file_progress(file_archive_transfer_t *state)
{
/* FIXME: this estimate is worse than before */
ptrdiff_t delta = state->directory - state->data;
return delta * 100 / state->zip_size;
return delta * 100 / state->archive_size;
}
/**
* file_archive_extract_first_content_file:
* @zip_path : filename path to ZIP archive.
* @zip_path_size : size of ZIP archive.
* @valid_exts : valid extensions for a content file.
* file_archive_extract_first_file:
* @archive_path : filename path to archive.
* @archive_path_size : size of archive.
* @valid_exts : valid extensions for the file.
* @extraction_directory : the directory to extract temporary
* unzipped content to.
* file to.
*
* Extract first content file from archive.
* Extract first file from archive.
*
* Returns : true (1) on success, otherwise false (0).
**/
bool file_archive_extract_first_content_file(
char *zip_path,
size_t zip_path_size,
bool file_archive_extract_first_file(
char *archive_path,
size_t archive_path_size,
const char *valid_exts,
const char *extraction_directory,
char *out_path, size_t len)
{
struct string_list *list = NULL;
bool ret = true;
struct zip_extract_userdata userdata = {0};
struct archive_extract_userdata userdata = {0};
/* We cannot unzip if the libretro
/* We cannot extract if the libretro
* implementation does not have any valid extensions. */
if (!valid_exts)
return false;
@ -585,12 +505,14 @@ bool file_archive_extract_first_content_file(
goto end;
}
userdata.zip_path = zip_path;
userdata.zip_path_size = zip_path_size;
userdata.archive_path = archive_path;
userdata.archive_path_size = archive_path_size;
userdata.extraction_directory = extraction_directory;
userdata.ext = list;
userdata.list = NULL;
userdata.context = NULL;
if (!file_archive_parse_file(zip_path, valid_exts,
if (!file_archive_parse_file(archive_path, valid_exts,
file_archive_extract_cb, &userdata))
{
/* Parsing file archive failed. */
@ -598,9 +520,9 @@ bool file_archive_extract_first_content_file(
goto end;
}
if (!userdata.found_content)
if (!userdata.found_file)
{
/* Didn't find any content that matched valid extensions
/* Didn't find any file that matched valid extensions
* for libretro implementation. */
ret = false;
goto end;
@ -626,20 +548,22 @@ end:
struct string_list *file_archive_get_file_list(const char *path,
const char *valid_exts)
{
struct string_list *list = string_list_new();
struct archive_extract_userdata userdata = {0};
if (!list)
userdata.list = string_list_new();
if (!userdata.list)
goto error;
if (!file_archive_parse_file(path, valid_exts,
file_archive_get_file_list_cb, list))
file_archive_get_file_list_cb, &userdata))
goto error;
return list;
return userdata.list;
error:
if (list)
string_list_free(list);
if (userdata.list)
string_list_free(userdata.list);
return NULL;
}
@ -649,16 +573,19 @@ bool file_archive_perform_mode(const char *path, const char *valid_exts,
{
switch (cmode)
{
case ZLIB_MODE_UNCOMPRESSED:
case ARCHIVE_MODE_UNCOMPRESSED:
if (!filestream_write_file(path, cdata, size))
goto error;
break;
case ZLIB_MODE_DEFLATE:
case ARCHIVE_MODE_COMPRESSED:
{
int ret = 0;
file_archive_file_handle_t handle = {0};
handle.backend = file_archive_get_default_file_backend();
struct archive_extract_userdata *data = (struct archive_extract_userdata*)userdata;
handle.backend = file_archive_get_file_backend(data->archive_path);
handle.stream = data->context;
if (!handle.backend->stream_decompress_data_to_file_init(&handle,
cdata, csize, size))
@ -685,7 +612,154 @@ error:
return false;
}
const struct file_archive_file_backend *file_archive_get_default_file_backend(void)
/* Generic compressed file loader.
* Extracts to buf, unless optional_filename != 0
* Then extracts to optional_filename and leaves buf alone.
*/
int file_archive_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 = file_archive_filename_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;
string_list_free(str_list);
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;
const struct file_archive_file_backend *backend =
file_archive_get_file_backend(str_list->elems[0].data);
*length = backend->compressed_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);
return ret;
error:
//RARCH_ERR("Could not extract string and substring from: %s.\n", path);
string_list_free(str_list);
*length = 0;
return 0;
}
struct string_list *file_archive_file_list_new(const char *path,
const char* ext)
{
#ifdef HAVE_COMPRESSION
const char* file_ext = path_get_extension(path);
#ifdef HAVE_7ZIP
if (string_is_equal_noncase(file_ext, "7z"))
return file_archive_get_file_list(path, ext);
#endif
#ifdef HAVE_ZLIB
if (string_is_equal_noncase(file_ext, "zip"))
return file_archive_get_file_list(path, ext);
#endif
#endif
return NULL;
}
/**
* file_archive_filename_split:
* @str : filename to turn into a string list
*
* Creates a new string list based on filename @path, delimited by a hash (#).
*
* Returns: new string list if successful, otherwise NULL.
*/
struct string_list *file_archive_filename_split(const char *path)
{
union string_list_elem_attr attr;
struct string_list *list = string_list_new();
const char *delim = NULL;
memset(&attr, 0, sizeof(attr));
delim = path_get_archive_delim(path);
if (delim)
{
/* add archive path to list first */
if (!string_list_append_n(list, path, delim - path, attr))
goto error;
/* now add the path within the archive */
delim++;
if (*delim)
{
if (!string_list_append(list, delim, attr))
goto error;
}
}
else
if (!string_list_append(list, path, attr))
goto error;
return list;
error:
string_list_free(list);
return NULL;
}
const struct file_archive_file_backend *file_archive_get_zlib_file_backend()
{
return &zlib_backend;
}
const struct file_archive_file_backend *file_archive_get_7z_file_backend()
{
return &sevenzip_backend;
}
const struct file_archive_file_backend* file_archive_get_file_backend(const char *path)
{
const char *file_ext = NULL;
char newpath[PATH_MAX_LENGTH] = {0};
strlcpy(newpath, path, sizeof(newpath));
char *last = (char*)path_get_archive_delim(newpath);
if (last)
*last = '\0';
file_ext = path_get_extension(newpath);
if (string_is_equal_noncase(file_ext, "7z"))
{
return &sevenzip_backend;
}
if (string_is_equal_noncase(file_ext, "zip"))
{
return &zlib_backend;
}
return NULL;
}

View File

@ -0,0 +1,405 @@
/* Copyright (C) 2010-2016 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (archive_file_sevenzip.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdlib.h>
#include <file/archive_file.h>
#include <streams/file_stream.h>
#include <retro_miscellaneous.h>
#include <encodings/utf.h>
#include <string/stdstring.h>
#include <lists/string_list.h>
#include <file/file_path.h>
#include <compat/strl.h>
#include "../../deps/7zip/7z.h"
#include "../../deps/7zip/7zAlloc.h"
#include "../../deps/7zip/7zCrc.h"
#include "../../deps/7zip/7zFile.h"
#define SEVENZIP_MAGIC "7z\xBC\xAF\x27\x1C"
#define SEVENZIP_MAGIC_LEN 6
struct sevenzip_context_t {
CFileInStream archiveStream;
CLookToRead lookStream;
ISzAlloc allocImp;
ISzAlloc allocTempImp;
CSzArEx db;
uint32_t index;
uint8_t *output;
file_archive_file_handle_t *handle;
};
static void* sevenzip_stream_new()
{
struct sevenzip_context_t *sevenzip_context =
(struct sevenzip_context_t*)calloc(1, sizeof(struct sevenzip_context_t));
/* These are the allocation routines - currently using
* the non-standard 7zip choices. */
sevenzip_context->allocImp.Alloc = SzAlloc;
sevenzip_context->allocImp.Free = SzFree;
sevenzip_context->allocTempImp.Alloc = SzAllocTemp;
sevenzip_context->allocTempImp.Free = SzFreeTemp;
return sevenzip_context;
}
static void sevenzip_stream_free(void *data)
{
struct sevenzip_context_t *sevenzip_context = (struct sevenzip_context_t*)data;
if (!sevenzip_context)
return;
SzArEx_Free(&sevenzip_context->db, &sevenzip_context->allocImp);
File_Close(&sevenzip_context->archiveStream.file);
}
/* 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 sevenzip_file_read(
const char *path,
const char *needle, void **buf,
const char *optional_outfile)
{
CFileInStream archiveStream;
CLookToRead lookStream;
ISzAlloc allocImp;
ISzAlloc allocTempImp;
CSzArEx db;
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] = {0};
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)
{
if (temp)
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 = SZ_ERROR_FAIL;
if (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;
}
}
if (temp)
free(temp);
IAlloc_Free(&allocImp, output);
if (!(file_found && res == SZ_OK))
{
/* Error handling */
/*if (!file_found)
RARCH_ERR("%s: %s in %s.\n",
msg_hash_to_str(MSG_FILE_NOT_FOUND),
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 bool sevenzip_stream_decompress_data_to_file_init(
file_archive_file_handle_t *handle,
const uint8_t *cdata, uint32_t csize, uint32_t size)
{
struct sevenzip_context_t *sevenzip_context =
(struct sevenzip_context_t*)handle->stream;
if (!sevenzip_context)
return false;
sevenzip_context->handle = handle;
return true;
}
static int sevenzip_stream_decompress_data_to_file_iterate(void *data)
{
struct sevenzip_context_t *sevenzip_context =
(struct sevenzip_context_t*)data;
uint32_t block_index = 0xFFFFFFFF;
size_t offset = 0;
size_t outSizeProcessed = 0;
size_t output_size = 0;
SRes res = SzArEx_Extract(&sevenzip_context->db, &sevenzip_context->lookStream.s, sevenzip_context->index, &block_index,
&sevenzip_context->output, &output_size, &offset, &outSizeProcessed,
&sevenzip_context->allocImp, &sevenzip_context->allocTempImp);
if (res != SZ_OK)
return -1;
if (sevenzip_context->handle)
sevenzip_context->handle->data = sevenzip_context->output;
return 1;
}
static int sevenzip_parse_file_init(file_archive_transfer_t *state,
const char *file)
{
if (state->archive_size < SEVENZIP_MAGIC_LEN)
return -1;
if (memcmp(state->data, SEVENZIP_MAGIC, SEVENZIP_MAGIC_LEN) != 0)
return -1;
struct sevenzip_context_t *sevenzip_context = sevenzip_stream_new();
if (InFile_Open(&sevenzip_context->archiveStream.file, file))
{
//RARCH_ERR("Could not open as 7zip archive: %s.\n",path);
return -1;
}
FileInStream_CreateVTable(&sevenzip_context->archiveStream);
LookToRead_CreateVTable(&sevenzip_context->lookStream, False);
sevenzip_context->lookStream.realStream = &sevenzip_context->archiveStream.s;
LookToRead_Init(&sevenzip_context->lookStream);
CrcGenerateTable();
SzArEx_Init(&sevenzip_context->db);
SzArEx_Open(&sevenzip_context->db, &sevenzip_context->lookStream.s, &sevenzip_context->allocImp, &sevenzip_context->allocTempImp);
state->stream = sevenzip_context;
return 0;
}
static int sevenzip_parse_file_iterate_step_internal(
file_archive_transfer_t *state, char *filename,
const uint8_t **cdata,
unsigned *cmode, uint32_t *size, uint32_t *csize,
uint32_t *checksum, unsigned *payback)
{
struct sevenzip_context_t *sevenzip_context = (struct sevenzip_context_t*)state->stream;
const CSzFileItem *file = sevenzip_context->db.db.Files + sevenzip_context->index;
if (sevenzip_context->index < sevenzip_context->db.db.NumFiles)
{
size_t len = SzArEx_GetFileNameUtf16(&sevenzip_context->db, sevenzip_context->index, NULL);
uint64_t compressed_size = sevenzip_context->db.db.PackSizes[sevenzip_context->index];
if (len < PATH_MAX_LENGTH && !file->IsDir)
{
char infile[PATH_MAX_LENGTH] = {0};
uint16_t *temp = (uint16_t*)malloc(len * sizeof(uint16_t));
if (!temp)
return -1;
SzArEx_GetFileNameUtf16(&sevenzip_context->db, sevenzip_context->index, temp);
SRes res = SZ_ERROR_FAIL;
if (temp)
{
res = utf16_to_char_string(temp, infile, sizeof(infile))
? SZ_OK : SZ_ERROR_FAIL;
free(temp);
}
if (res != SZ_OK)
return -1;
strlcpy(filename, infile, sizeof(infile));
*cmode = ARCHIVE_MODE_COMPRESSED;
*checksum = file->Crc;
*size = file->Size;
*csize = compressed_size;
}
}
*payback = 1;
return 1;
}
static int sevenzip_parse_file_iterate_step(file_archive_transfer_t *state,
const char *valid_exts, void *userdata, file_archive_file_cb file_cb)
{
const uint8_t *cdata = NULL;
uint32_t checksum = 0;
uint32_t size = 0;
uint32_t csize = 0;
unsigned cmode = 0;
unsigned payload = 0;
char filename[PATH_MAX_LENGTH] = {0};
int ret = sevenzip_parse_file_iterate_step_internal(state, filename,
&cdata, &cmode, &size, &csize,
&checksum, &payload);
if (ret != 1)
return ret;
if (!file_cb(filename, valid_exts, cdata, cmode,
csize, size, checksum, userdata))
return 0;
struct sevenzip_context_t *sevenzip_context =
(struct sevenzip_context_t*)state->stream;
sevenzip_context->index += payload;
return 1;
}
static uint32_t sevenzip_stream_crc32_calculate(uint32_t crc,
const uint8_t *data, size_t length)
{
return CrcUpdate(crc, data, length);
}
const struct file_archive_file_backend sevenzip_backend = {
sevenzip_stream_new,
sevenzip_stream_free,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
sevenzip_stream_decompress_data_to_file_init,
sevenzip_stream_decompress_data_to_file_iterate,
NULL,
NULL,
NULL,
sevenzip_stream_crc32_calculate,
sevenzip_file_read,
sevenzip_parse_file_init,
sevenzip_parse_file_iterate_step,
"7z"
};

View File

@ -25,8 +25,18 @@
#include <compat/zlib.h>
#include <file/archive_file.h>
#include <streams/file_stream.h>
#include <string.h>
#include <retro_miscellaneous.h>
static void *zlib_stream_new(void)
#ifndef CENTRAL_FILE_HEADER_SIGNATURE
#define CENTRAL_FILE_HEADER_SIGNATURE 0x02014b50
#endif
#ifndef END_OF_CENTRAL_DIR_SIGNATURE
#define END_OF_CENTRAL_DIR_SIGNATURE 0x06054b50
#endif
static void* zlib_stream_new(void)
{
return (z_stream*)calloc(1, sizeof(z_stream));
}
@ -138,7 +148,7 @@ static bool zlib_stream_decompress_data_to_file_init(
if (!(handle->stream = (z_stream*)zlib_stream_new()))
goto error;
if (inflateInit2((z_streamp)handle->stream, -MAX_WBITS) != Z_OK)
goto error;
@ -197,6 +207,265 @@ static uint32_t zlib_stream_crc32_calculate(uint32_t crc,
return crc32(crc, data, length);
}
struct decomp_state
{
char *opt_file;
char *needle;
void **buf;
size_t size;
bool found;
};
static bool 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 = &zlib_backend;
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("%s\n", msg_hash_to_str(MSG_INFLATED_CHECKSUM_DID_NOT_MATCH_CRC32));
goto error;
}
if (handle->stream)
free(handle->stream);
return true;
error:
if (handle->stream)
free(handle->stream);
if (handle->data)
free(handle->data);
handle->stream = NULL;
handle->data = NULL;
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 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 (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("%s: %s\n",
msg_hash_to_str(MSG_EXTRACTING_FILE),
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 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 = ARCHIVE_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,
"", 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;
}
static int zip_parse_file_init(file_archive_transfer_t *state,
const char *file)
{
if (state->archive_size < 22)
return -1;
state->footer = state->data + state->archive_size - 22;
for (;; state->footer--)
{
if (state->footer <= state->data + 22)
return -1;
if (read_le(state->footer, 4) == END_OF_CENTRAL_DIR_SIGNATURE)
{
unsigned comment_len = read_le(state->footer + 20, 2);
if (state->footer + 22 + comment_len == state->data + state->archive_size)
break;
}
}
state->directory = state->data + read_le(state->footer + 16, 4);
return 0;
}
static int zip_parse_file_iterate_step_internal(
file_archive_transfer_t *state, char *filename,
const uint8_t **cdata,
unsigned *cmode, uint32_t *size, uint32_t *csize,
uint32_t *checksum, unsigned *payback)
{
uint32_t offset;
uint32_t namelength, extralength, commentlength,
offsetNL, offsetEL;
uint32_t signature = read_le(state->directory + 0, 4);
if (signature != CENTRAL_FILE_HEADER_SIGNATURE)
return 0;
*cmode = read_le(state->directory + 10, 2); // compression mode, 0 = store, 8 = deflate
*checksum = read_le(state->directory + 16, 4); // CRC32
*csize = read_le(state->directory + 20, 4); // compressed size
*size = read_le(state->directory + 24, 4); // uncompressed size
namelength = read_le(state->directory + 28, 2); // file name length
extralength = read_le(state->directory + 30, 2); // extra field length
commentlength = read_le(state->directory + 32, 2); // file comment length
if (namelength >= PATH_MAX_LENGTH)
return -1;
memcpy(filename, state->directory + 46, namelength); // file name
offset = read_le(state->directory + 42, 4); // relative offset of local file header
offsetNL = read_le(state->data + offset + 26, 2); // file name length
offsetEL = read_le(state->data + offset + 28, 2); // extra field length
*cdata = state->data + offset + 30 + offsetNL + offsetEL;
*payback = 46 + namelength + extralength + commentlength;
return 1;
}
static int zip_parse_file_iterate_step(file_archive_transfer_t *state,
const char *valid_exts, void *userdata, file_archive_file_cb file_cb)
{
const uint8_t *cdata = NULL;
uint32_t checksum = 0;
uint32_t size = 0;
uint32_t csize = 0;
unsigned cmode = 0;
unsigned payload = 0;
char filename[PATH_MAX_LENGTH] = {0};
int ret = zip_parse_file_iterate_step_internal(state, filename,
&cdata, &cmode, &size, &csize,
&checksum, &payload);
if (ret != 1)
return ret;
if (!file_cb(filename, valid_exts, cdata, cmode,
csize, size, checksum, userdata))
return 0;
state->directory += payload;
return 1;
}
const struct file_archive_file_backend zlib_backend = {
zlib_stream_new,
zlib_stream_free,
@ -212,5 +481,8 @@ const struct file_archive_file_backend zlib_backend = {
zlib_stream_compress_free,
zlib_stream_compress_data_to_file,
zlib_stream_crc32_calculate,
zip_file_read,
zip_parse_file_init,
zip_parse_file_iterate_step,
"zlib"
};

View File

@ -194,12 +194,12 @@ bool path_is_compressed_file(const char* path)
const char *ext = path_get_extension(path);
#ifdef HAVE_ZLIB
if (strcasestr(ext, "zip"))
if (string_is_equal_noncase(ext, "zip"))
return true;
#endif
#ifdef HAVE_7ZIP
if (strcasestr(ext, "7z"))
if (string_is_equal_noncase(ext, "7z"))
return true;
#endif

View File

@ -870,7 +870,7 @@ static struct rpng_process *rpng_process_init(rpng_t *rpng, unsigned *width, uns
if (!process)
return NULL;
process->stream_backend = file_archive_get_default_file_backend();
process->stream_backend = file_archive_get_zlib_file_backend();
png_pass_geom(&rpng->ihdr, rpng->ihdr.width,
rpng->ihdr.height, NULL, NULL, &process->inflate_buf_size);

View File

@ -48,7 +48,7 @@ static bool png_write_crc(RFILE *file, const uint8_t *data, size_t size)
{
uint8_t crc_raw[4] = {0};
const struct file_archive_file_backend *stream_backend =
file_archive_get_default_file_backend();
file_archive_get_zlib_file_backend();
uint32_t crc = stream_backend->stream_crc_calculate(0, data, size);
dword_write_be(crc_raw, crc);
@ -230,7 +230,7 @@ static bool rpng_save_image(const char *path,
if (!file)
GOTO_END_ERROR();
stream_backend = file_archive_get_default_file_backend();
stream_backend = file_archive_get_zlib_file_backend();
if (filestream_write(file, png_magic, sizeof(png_magic)) != sizeof(png_magic))
GOTO_END_ERROR();

View File

@ -42,4 +42,9 @@ const char *utf8skip(const char *str, size_t chars);
uint32_t utf8_walk(const char **string);
bool utf16_to_char(uint8_t **utf_data,
size_t *dest_len, const uint16_t *in);
bool utf16_to_char_string(const uint16_t *in, char *s, size_t len);
#endif

View File

@ -30,21 +30,56 @@
enum file_archive_transfer_type
{
ZLIB_TRANSFER_NONE = 0,
ZLIB_TRANSFER_INIT,
ZLIB_TRANSFER_ITERATE,
ZLIB_TRANSFER_DEINIT,
ZLIB_TRANSFER_DEINIT_ERROR
ARCHIVE_TRANSFER_NONE = 0,
ARCHIVE_TRANSFER_INIT,
ARCHIVE_TRANSFER_ITERATE,
ARCHIVE_TRANSFER_DEINIT,
ARCHIVE_TRANSFER_DEINIT_ERROR
};
typedef struct file_archive_handle
{
void *stream;
uint8_t *data;
uint8_t *data;
uint32_t real_checksum;
const struct file_archive_file_backend *backend;
} file_archive_file_handle_t;
typedef struct file_archive_transfer
{
void *handle;
void *stream;
const uint8_t *footer;
const uint8_t *directory;
const uint8_t *data;
int32_t archive_size;
enum file_archive_transfer_type type;
const struct file_archive_file_backend *backend;
} file_archive_transfer_t;
enum file_archive_compression_mode
{
ARCHIVE_MODE_UNCOMPRESSED = 0,
ARCHIVE_MODE_COMPRESSED = 8
};
struct archive_extract_userdata
{
char *archive_path;
char *first_extracted_file_path;
const char *extraction_directory;
size_t archive_path_size;
struct string_list *ext;
struct string_list *list;
bool found_file;
void *context;
};
/* Returns true when parsing should continue. False to stop. */
typedef int (*file_archive_file_cb)(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 file_archive_file_backend
{
void *(*stream_new)(void);
@ -63,26 +98,19 @@ struct file_archive_file_backend
void (*stream_compress_free)(void *);
int (*stream_compress_data_to_file)(void *);
uint32_t (*stream_crc_calculate)(uint32_t, const uint8_t *, size_t);
int (*compressed_file_read)(const char *path, const char *needle, void **buf,
const char *optional_outfile);
int (*archive_parse_file_init)(
file_archive_transfer_t *state,
const char *file);
int (*archive_parse_file_iterate_step)(
file_archive_transfer_t *state,
const char *valid_exts,
void *userdata,
file_archive_file_cb file_cb);
const char *ident;
};
typedef struct file_archive_transfer
{
void *handle;
const uint8_t *footer;
const uint8_t *directory;
const uint8_t *data;
int32_t zip_size;
enum file_archive_transfer_type type;
const struct file_archive_file_backend *backend;
} file_archive_transfer_t;
/* Returns true when parsing should continue. False to stop. */
typedef int (*file_archive_file_cb)(const char *name, const char *valid_exts,
const uint8_t *cdata, unsigned cmode, uint32_t csize, uint32_t size,
uint32_t crc32, void *userdata);
int file_archive_parse_file_iterate(
file_archive_transfer_t *state,
bool *returnerr,
@ -96,43 +124,60 @@ void file_archive_parse_file_iterate_stop(file_archive_transfer_t *state);
int file_archive_parse_file_progress(file_archive_transfer_t *state);
/**
* file_archive_extract_first_content_file:
* file_archive_extract_first_file:
* @zip_path : filename path to ZIP archive.
* @zip_path_size : size of ZIP archive.
* @valid_exts : valid extensions for a content file.
* @valid_exts : valid extensions for a file.
* @extraction_directory : the directory to extract temporary
* unzipped content to.
* unzipped file to.
*
* Extract first content file from archive.
* Extract first file from archive.
*
* Returns : true (1) on success, otherwise false (0).
**/
bool file_archive_extract_first_content_file(char *zip_path, size_t zip_path_size,
bool file_archive_extract_first_file(char *zip_path, size_t zip_path_size,
const char *valid_exts, const char *extraction_dir,
char *out_path, size_t len);
/**
* file_archive_get_file_list:
* @path : filename path of archive
* @valid_exts : Valid extensions of archive to be parsed.
* @valid_exts : Valid extensions of archive to be parsed.
* If NULL, allow all.
*
* Returns: string listing of files from archive on success, otherwise NULL.
**/
struct string_list *file_archive_get_file_list(const char *path, const char *valid_exts);
struct string_list* file_archive_get_file_list(const char *path, const char *valid_exts);
bool file_archive_perform_mode(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 string_list *compressed_file_list_new(const char *filename,
const char* ext);
void file_archive_deflate_init(void *data, int level);
const struct file_archive_file_backend *file_archive_get_default_file_backend(void);
int file_archive_compressed_read(
const char* path, void **buf,
const char* optional_filename, ssize_t *length);
struct string_list* file_archive_file_list_new(const char *path,
const char *ext);
struct string_list* file_archive_filename_split(const char *path);
const uint8_t* file_archive_data(void *handle);
int file_archive_parse_file_init(file_archive_transfer_t *state,
const char *file);
void file_archive_free(void *handle);
const struct file_archive_file_backend* file_archive_get_zlib_file_backend(void);
const struct file_archive_file_backend* file_archive_get_7z_file_backend(void);
const struct file_archive_file_backend* file_archive_get_file_backend(const char *path);
extern const struct file_archive_file_backend zlib_backend;
extern const struct file_archive_file_backend sevenzip_backend;
#endif

View File

@ -44,7 +44,7 @@ struct string_list_elem
union string_list_elem_attr attr;
};
struct string_list
struct __attribute__ ((aligned(1))) string_list
{
struct string_list_elem *elems;
size_t size;
@ -138,7 +138,7 @@ void string_list_free(struct string_list *list);
* @list : pointer to string list.
* @delim : delimiter character for @list.
*
* A string list will be joined/concatenated as a
* A string list will be joined/concatenated as a
* string to @buffer, delimited by @delim.
*/
void string_list_join_concat(char *buffer, size_t size,

View File

@ -33,7 +33,7 @@
#elif defined(GEKKO) || defined(__PSL1GHT__) || defined(__QNX__)
#include <unistd.h>
#elif defined(PSP)
#include <pspthreadman.h>
#include <pspthreadman.h>
#elif defined(VITA)
#include <psp2/kernel/threadmgr.h>
#elif defined(_3DS)
@ -159,6 +159,18 @@ static INLINE float db_to_gain(float db)
return powf(10.0f, db / 20.0f);
}
static INLINE uint32_t read_le(const uint8_t *data, unsigned size)
{
unsigned i;
uint32_t val = 0;
size *= 8;
for (i = 0; i < size; i += 8)
val |= (uint32_t)*data++ << i;
return val;
}
/* Helper macros and struct to keep track of many booleans.
* To check for multiple bits, use &&, not &.
* For OR, | can be used. */

View File

@ -186,7 +186,7 @@ void string_list_set(struct string_list *list,
* @list : pointer to string list.
* @delim : delimiter character for @list.
*
* A string list will be joined/concatenated as a
* A string list will be joined/concatenated as a
* string to @buffer, delimited by @delim.
*/
void string_list_join_concat(char *buffer, size_t size,

View File

@ -3372,7 +3372,9 @@ static int menu_displaylist_parse_generic(
filter_ext = true;
if (path_is_compressed)
str_list = compressed_file_list_new(info->path, info->exts);
{
str_list = file_archive_file_list_new(info->path, info->exts);
}
else
str_list = dir_list_new(info->path,
filter_ext ? info->exts : NULL,

View File

@ -198,6 +198,8 @@ uint32_t msg_hash_calculate(const char *s)
#define FILE_HASH_APK 0x0b885e61U
#define HASH_EXTENSION_7Z 0x005971d6U
#define HASH_EXTENSION_7Z_UPP 0x005971b6U
#define HASH_EXTENSION_ZIP 0x0b88c7d8U
#define HASH_EXTENSION_ZIP_UPP 0x0b883b78U
#define HASH_EXTENSION_CUE 0x0b886782U
@ -211,6 +213,8 @@ enum msg_file_type msg_hash_to_file_type(uint32_t hash)
switch (hash)
{
case MENU_VALUE_COMP:
case HASH_EXTENSION_7Z:
case HASH_EXTENSION_7Z_UPP:
case HASH_EXTENSION_ZIP:
case HASH_EXTENSION_ZIP_UPP:
case FILE_HASH_APK:

12
patch.c
View File

@ -90,7 +90,7 @@ static uint8_t bps_read(struct bps_data *bps)
uint8_t data = bps->modify_data[bps->modify_offset++];
#ifdef HAVE_ZLIB
const struct file_archive_file_backend *stream_backend =
file_archive_get_default_file_backend();
file_archive_get_zlib_file_backend();
bps->modify_checksum = ~stream_backend->stream_crc_calculate(
~bps->modify_checksum, &data, 1);
#endif
@ -118,7 +118,7 @@ static void bps_write(struct bps_data *bps, uint8_t data)
{
#ifdef HAVE_ZLIB
const struct file_archive_file_backend *stream_backend =
file_archive_get_default_file_backend();
file_archive_get_zlib_file_backend();
#endif
if (!bps)
return;
@ -142,7 +142,7 @@ static enum patch_error bps_apply_patch(
modify_modify_checksum = 0, checksum;
#ifdef HAVE_ZLIB
const struct file_archive_file_backend *stream_backend =
file_archive_get_default_file_backend();
file_archive_get_zlib_file_backend();
#endif
if (modify_length < 19)
@ -254,7 +254,7 @@ static uint8_t ups_patch_read(struct ups_data *data)
{
#ifdef HAVE_ZLIB
const struct file_archive_file_backend *stream_backend =
file_archive_get_default_file_backend();
file_archive_get_zlib_file_backend();
#endif
if (data && data->patch_offset < data->patch_length)
@ -273,7 +273,7 @@ static uint8_t ups_source_read(struct ups_data *data)
{
#ifdef HAVE_ZLIB
const struct file_archive_file_backend *stream_backend =
file_archive_get_default_file_backend();
file_archive_get_zlib_file_backend();
#endif
if (data && data->source_offset < data->source_length)
@ -292,7 +292,7 @@ static void ups_target_write(struct ups_data *data, uint8_t n)
{
#ifdef HAVE_ZLIB
const struct file_archive_file_backend *stream_backend =
file_archive_get_default_file_backend();
file_archive_get_zlib_file_backend();
#endif
if (data && data->target_offset < data->target_length)

View File

@ -90,18 +90,10 @@
#include "../paths.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_CHEEVOS
#include "../cheevos.h"
#endif
typedef struct content_stream
{
uint32_t a;
@ -116,607 +108,6 @@ static bool _content_is_inited = false;
static bool core_does_not_need_content = false;
static uint32_t content_crc = 0;
#ifdef HAVE_COMPRESSION
/**
* filename_split_archive:
* @str : filename to turn into a string list
*
* Creates a new string list based on filename @path, delimited by a hash (#).
*
* Returns: new string list if successful, otherwise NULL.
*/
static struct string_list *filename_split_archive(const char *path)
{
union string_list_elem_attr attr;
struct string_list *list = string_list_new();
const char *delim = NULL;
memset(&attr, 0, sizeof(attr));
delim = path_get_archive_delim(path);
if (delim)
{
/* add archive path to list first */
if (!string_list_append_n(list, path, delim - path, attr))
goto error;
/* now add the path within the archive */
delim++;
if (*delim)
{
if (!string_list_append(list, delim, attr))
goto error;
}
}
else
if (!string_list_append(list, path, attr))
goto error;
return list;
error:
string_list_free(list);
return NULL;
}
#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;
ISzAlloc allocImp;
ISzAlloc allocTempImp;
CSzArEx db;
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] = {0};
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 = SZ_ERROR_FAIL;
if (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("%s: %s in %s.\n",
msg_hash_to_str(MSG_FILE_NOT_FOUND),
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;
ISzAlloc allocImp;
ISzAlloc allocTempImp;
CSzArEx db;
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 as 7zip archive: %s.\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] = {0};
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 = SZ_ERROR_FAIL;
if (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("%s\n",
msg_hash_to_str(MSG_INFLATED_CHECKSUM_DID_NOT_MATCH_CRC32));
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("%s: %s\n",
msg_hash_to_str(MSG_EXTRACTING_FILE),
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 = filename_split_archive(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;
string_list_free(str_list);
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;
}
#endif
string_list_free(str_list);
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.
@ -724,7 +115,7 @@ error:
* 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
* Read the contents of a file into @buf. Will call file_archive_compressed_read
* if path contains a compressed file, otherwise will call filestream_read_file().
*
* Returns: 1 if file read, 0 on error.
@ -734,31 +125,13 @@ 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))
if (file_archive_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] = {0};
@ -939,7 +312,7 @@ static void content_load_init_wrap(
* If no content file can be loaded, will start up RetroArch
* as-is.
*
* Returns: false (0) if retroarch_main_init failed,
* Returns: false (0) if retroarch_main_init failed,
* otherwise true (1).
**/
static bool content_load(content_ctx_info_t *info)
@ -1046,7 +419,7 @@ static bool read_content_file(unsigned i, const char *path, void **buf,
stream_info.c = *length;
if (!stream_backend)
stream_backend = file_archive_get_default_file_backend();
stream_backend = file_archive_get_zlib_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;
@ -1167,7 +540,7 @@ static bool load_content_from_compressed_archive(
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);
ret = file_archive_compressed_read(path, NULL, new_path, &new_path_len);
if (!ret || new_path_len < 0)
{
@ -1178,7 +551,7 @@ static bool load_content_from_compressed_archive(
}
string_list_append(additional_path_allocs, new_path, attributes);
info[i].path =
info[i].path =
additional_path_allocs->elems[additional_path_allocs->size -1 ].data;
if (!string_list_append(temporary_content, new_path, attributes))
@ -1281,7 +654,7 @@ 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 =
const struct retro_subsystem_info *special =
libretro_find_subsystem_info(system->subsystem.data,
system->subsystem.size, global->subsystem);
@ -1326,7 +699,7 @@ error:
return NULL;
}
#ifdef HAVE_ZLIB
#ifdef HAVE_COMPRESSION
static bool init_content_file_extract(
struct string_list *temporary_content,
struct string_list *content,
@ -1340,52 +713,49 @@ static bool init_content_file_extract(
for (i = 0; i < content->size; i++)
{
const char *ext = NULL;
bool compressed = 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);
compressed = path_contains_compressed_file(content->elems[i].data);
if (special)
valid_ext = special->roms[i].valid_extensions;
if (!ext)
if (!compressed)
continue;
if (string_is_equal_noncase(ext, "zip"))
char new_path[PATH_MAX_LENGTH] = {0};
char temp_content[PATH_MAX_LENGTH] = {0};
strlcpy(temp_content, content->elems[i].data,
sizeof(temp_content));
if (!file_archive_extract_first_file(temp_content,
sizeof(temp_content), valid_ext,
*settings->directory.cache ?
settings->directory.cache : NULL,
new_path, sizeof(new_path)))
{
char new_path[PATH_MAX_LENGTH] = {0};
char temp_content[PATH_MAX_LENGTH] = {0};
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("%s: %s.\n",
msg_hash_to_str(
MSG_FAILED_TO_EXTRACT_CONTENT_FROM_COMPRESSED_FILE),
temp_content);
runloop_msg_queue_push(
msg_hash_to_str(MSG_FAILED_TO_EXTRACT_CONTENT_FROM_COMPRESSED_FILE)
, 2, 180, true);
return false;
}
string_list_set(content, i, new_path);
if (!string_list_append(temporary_content,
new_path, *attr))
return false;
RARCH_ERR("%s: %s.\n",
msg_hash_to_str(
MSG_FAILED_TO_EXTRACT_CONTENT_FROM_COMPRESSED_FILE),
temp_content);
runloop_msg_queue_push(
msg_hash_to_str(MSG_FAILED_TO_EXTRACT_CONTENT_FROM_COMPRESSED_FILE)
, 2, 180, true);
return false;
}
string_list_set(content, i, new_path);
if (!string_list_append(temporary_content,
new_path, *attr))
return false;
}
return true;
}
#endif
@ -1435,7 +805,7 @@ static bool init_content_file_set_attribs(
}
}
#ifdef HAVE_ZLIB
#ifdef HAVE_COMPRESSION
/* Try to extract all content we're going to load if appropriate. */
if (!init_content_file_extract(temporary_content,
content, system, special, &attr))
@ -1486,7 +856,7 @@ static bool content_file_init(struct string_list *temporary_content)
additional_path_allocs = string_list_new();
ret = load_content(temporary_content,
info, content, special, additional_path_allocs);
info, content, special, additional_path_allocs);
for (i = 0; i < content->size; i++)
free((void*)info[i].data);
@ -1580,7 +950,6 @@ error:
return false;
}
#ifdef HAVE_MENU
static void menu_content_environment_get(int *argc, char *argv[],
void *args, void *params_data)
@ -1628,7 +997,7 @@ static void menu_content_environment_get(int *argc, char *argv[],
*
* Returns: true (1) if successful, otherwise false (0).
**/
static bool task_load_content(content_ctx_info_t *content_info,
static bool task_load_content(content_ctx_info_t *content_info,
bool launched_from_menu,
enum content_mode_load mode)
{
@ -1652,7 +1021,7 @@ static bool task_load_content(content_ctx_info_t *content_info,
/** Show loading OSD message */
if (!string_is_empty(fullpath))
{
snprintf(msg, sizeof(msg), "%s %s ...",
snprintf(msg, sizeof(msg), "%s %s ...",
msg_hash_to_str(MSG_LOADING),
name);
runloop_msg_queue_push(msg, 2, 1, true);
@ -1723,7 +1092,7 @@ static bool task_load_content(content_ctx_info_t *content_info,
break;
}
if (content_push_to_history_playlist(playlist_tmp, tmp,
if (content_push_to_history_playlist(playlist_tmp, tmp,
core_name, core_path))
playlist_write_file(playlist_tmp);
}
@ -1905,7 +1274,7 @@ bool task_push_content_load_default(
break;
}
/* On targets that have no dynamic core loading support, we'd
/* On targets that have no dynamic core loading support, we'd
* execute the new core from this point. If this returns false,
* we assume we can dynamically load the core. */
switch (mode)
@ -1943,7 +1312,7 @@ bool task_push_content_load_default(
/* Fork core? */
switch (mode)
{
case CONTENT_MODE_LOAD_NOTHING_WITH_NEW_CORE_FROM_MENU:
case CONTENT_MODE_LOAD_NOTHING_WITH_NEW_CORE_FROM_MENU:
if (!frontend_driver_set_fork(FRONTEND_FORK_CORE))
return false;
break;
@ -1952,7 +1321,7 @@ bool task_push_content_load_default(
}
#endif
/* Preliminary stuff that has to be done before we
/* Preliminary stuff that has to be done before we
* load the actual content. Can differ per mode. */
switch (mode)
{

View File

@ -47,9 +47,9 @@ typedef struct database_state_handle
size_t list_index;
size_t entry_index;
uint32_t crc;
uint32_t zip_crc;
uint32_t archive_crc;
uint8_t *buf;
char zip_name[PATH_MAX_LENGTH];
char archive_name[PATH_MAX_LENGTH];
char serial[4096];
} database_state_handle_t;
@ -69,10 +69,10 @@ static int zlib_compare_crc32(const char *name, const char *valid_exts,
uint32_t crc32, void *userdata)
{
database_state_handle_t *db_state = (database_state_handle_t*)userdata;
db_state->crc = crc32;
strlcpy(db_state->zip_name, name, sizeof(db_state->zip_name));
strlcpy(db_state->archive_name, name, sizeof(db_state->archive_name));
#if 0
RARCH_LOG("Going to compare CRC 0x%x for %s\n", crc32, name);
@ -145,7 +145,7 @@ static int cue_get_serial(database_state_handle_t *db_state,
char track_path[PATH_MAX_LENGTH] = {0};
int rv = find_first_data_track(name,
&offset, track_path, PATH_MAX_LENGTH);
if (rv < 0)
{
RARCH_LOG("%s: %s\n",
@ -167,8 +167,8 @@ static bool file_get_crc(database_state_handle_t *db_state,
name, (void**)&db_state->buf, &ret);
#ifdef HAVE_ZLIB
const struct file_archive_file_backend *stream_backend =
file_archive_get_default_file_backend();
const struct file_archive_file_backend *stream_backend =
file_archive_get_zlib_file_backend();
#endif
if (read_from != 1 || ret <= 0)
@ -193,12 +193,12 @@ static int task_database_iterate_playlist(
switch (msg_hash_to_file_type(msg_hash_calculate(path_get_extension(name))))
{
case FILE_TYPE_COMPRESSED:
#ifdef HAVE_ZLIB
db->type = DATABASE_TYPE_ITERATE_ZIP;
#ifdef HAVE_COMPRESSION
db->type = DATABASE_TYPE_ITERATE_ARCHIVE;
memset(&db->state, 0, sizeof(file_archive_transfer_t));
db_state->zip_name[0] = '\0';
db->state.type = ZLIB_TRANSFER_INIT;
return file_get_crc(db_state, name, &db_state->zip_crc);
db_state->archive_name[0] = '\0';
db->state.type = ARCHIVE_TRANSFER_INIT;
return file_get_crc(db_state, name, &db_state->archive_crc);
#else
break;
#endif
@ -226,7 +226,7 @@ static int task_database_iterate_playlist(
static int database_info_list_iterate_end_no_match(
database_state_handle_t *db_state)
{
/* Reached end of database list,
/* Reached end of database list,
* CRC match probably didn't succeed. */
db_state->list_index = 0;
db_state->entry_index = 0;
@ -251,7 +251,7 @@ static int database_info_list_iterate_new(database_state_handle_t *db_state,
{
const char *new_database = db_state->list->elems[db_state->list_index].data;
#if 0
RARCH_LOG("Check database [%d/%d] : %s\n", (unsigned)db_state->list_index,
RARCH_LOG("Check database [%d/%d] : %s\n", (unsigned)db_state->list_index,
(unsigned)db_state->list->size, new_database);
#endif
if (db_state->info)
@ -263,7 +263,7 @@ static int database_info_list_iterate_new(database_state_handle_t *db_state,
static int database_info_list_iterate_found_match(
database_state_handle_t *db_state,
database_info_handle_t *db,
const char *zip_name
const char *archive_name
)
{
char db_crc[PATH_MAX_LENGTH] = {0};
@ -272,11 +272,11 @@ static int database_info_list_iterate_found_match(
char entry_path_str[PATH_MAX_LENGTH] = {0};
playlist_t *playlist = NULL;
settings_t *settings = config_get_ptr();
const char *db_path =
const char *db_path =
db_state->list->elems[db_state->list_index].data;
const char *entry_path = db ?
const char *entry_path = db ?
db->list->elems[db->list_ptr].data : NULL;
database_info_t *db_info_entry =
database_info_t *db_info_entry =
&db_state->info->list[db_state->entry_index];
fill_short_pathname_representation_noext(db_playlist_base_str,
@ -296,8 +296,8 @@ static int database_info_list_iterate_found_match(
if (entry_path)
strlcpy(entry_path_str, entry_path, sizeof(entry_path_str));
if (!string_is_empty(zip_name))
fill_pathname_join_delim(entry_path_str, entry_path_str, zip_name,
if (!string_is_empty(archive_name))
fill_pathname_join_delim(entry_path_str, entry_path_str, archive_name,
'#', sizeof(entry_path_str));
#if 0
@ -308,7 +308,7 @@ static int database_info_list_iterate_found_match(
RARCH_LOG("Playlist Path: %s\n", db_playlist_path);
RARCH_LOG("Entry Path: %s\n", entry_path);
RARCH_LOG("Playlist not NULL: %d\n", playlist != NULL);
RARCH_LOG("ZIP entry: %s\n", zip_name);
RARCH_LOG("ZIP entry: %s\n", archive_name);
RARCH_LOG("entry path str: %s\n", entry_path_str);
#endif
@ -332,7 +332,7 @@ static int database_info_list_iterate_found_match(
return 0;
}
/* End of entries in database info list and didn't find a
/* End of entries in database info list and didn't find a
* match, go to the next database. */
static int database_info_list_iterate_next(
database_state_handle_t *db_state
@ -353,7 +353,7 @@ static int task_database_iterate_crc_lookup(
const char *zip_entry)
{
if (!db_state->list ||
if (!db_state->list ||
(unsigned)db_state->list_index == (unsigned)db_state->list->size)
return database_info_list_iterate_end_no_match(db_state);
@ -362,14 +362,14 @@ static int task_database_iterate_crc_lookup(
char query[50] = {0};
snprintf(query, sizeof(query),
"{crc:or(b\"%08X\",b\"%08X\")}",
swap_if_big32(db_state->crc), swap_if_big32(db_state->zip_crc));
swap_if_big32(db_state->crc), swap_if_big32(db_state->archive_crc));
database_info_list_iterate_new(db_state, query);
}
if (db_state->info)
{
database_info_t *db_info_entry =
database_info_t *db_info_entry =
&db_state->info->list[db_state->entry_index];
if (db_info_entry && db_info_entry->crc32)
@ -378,7 +378,7 @@ static int task_database_iterate_crc_lookup(
RARCH_LOG("CRC32: 0x%08X , entry CRC32: 0x%08X (%s).\n",
db_state->crc, db_info_entry->crc32, db_info_entry->name);
#endif
if (db_state->zip_crc == db_info_entry->crc32)
if (db_state->archive_crc == db_info_entry->crc32)
return database_info_list_iterate_found_match(
db_state, db, NULL);
if (db_state->crc == db_info_entry->crc32)
@ -404,7 +404,7 @@ static int task_database_iterate_crc_lookup(
return 0;
}
static int task_database_iterate_playlist_zip(
static int task_database_iterate_playlist_archive(
database_state_handle_t *db_state,
database_info_handle_t *db, const char *name)
{
@ -412,7 +412,7 @@ static int task_database_iterate_playlist_zip(
#ifdef HAVE_ZLIB
if (db_state->crc != 0)
return task_database_iterate_crc_lookup(
db_state, db, db_state->zip_name);
db_state, db, db_state->archive_name);
if (file_archive_parse_file_iterate(&db->state,
&returnerr, name, NULL, zlib_compare_crc32,
@ -468,14 +468,14 @@ static int task_database_iterate_serial_lookup(
database_state_handle_t *db_state,
database_info_handle_t *db, const char *name)
{
if (!db_state->list ||
if (!db_state->list ||
(unsigned)db_state->list_index == (unsigned)db_state->list->size)
return database_info_list_iterate_end_no_match(db_state);
if (db_state->entry_index == 0)
{
char query[50] = {0};
char *serial_buf =
char *serial_buf =
bin_to_hex_alloc((uint8_t*)db_state->serial, 10 * sizeof(uint8_t));
if (!serial_buf)
@ -536,8 +536,8 @@ static int task_database_iterate(database_state_handle_t *db_state,
{
case DATABASE_TYPE_ITERATE:
return task_database_iterate_playlist(db_state, db, name);
case DATABASE_TYPE_ITERATE_ZIP:
return task_database_iterate_playlist_zip(db_state, db, name);
case DATABASE_TYPE_ITERATE_ARCHIVE:
return task_database_iterate_playlist_archive(db_state, db, name);
case DATABASE_TYPE_ITERATE_LUTRO:
return task_database_iterate_playlist_lutro(db_state, db, name);
case DATABASE_TYPE_SERIAL_LOOKUP:
@ -569,7 +569,7 @@ static void task_database_handler(retro_task_t *task)
db_handle_t *db = (db_handle_t*)task->state;
database_info_handle_t *dbinfo = db->handle;
database_state_handle_t *dbstate = &db->state;
const char *name = dbinfo ?
const char *name = dbinfo ?
dbinfo->list->elems[dbinfo->list_ptr].data : NULL;
if (!dbinfo || task->cancelled)

View File

@ -35,7 +35,7 @@ typedef struct
char *callback_error;
file_archive_transfer_t zlib;
file_archive_transfer_t archive;
} decompress_state_t;
static int file_decompressed_target_file(const char *name,
@ -159,16 +159,16 @@ static void task_decompress_handler(retro_task_t *task)
{
bool retdec = false;
decompress_state_t *dec = (decompress_state_t*)task->state;
int ret = file_archive_parse_file_iterate(&dec->zlib,
int ret = file_archive_parse_file_iterate(&dec->archive,
&retdec, dec->source_file,
dec->valid_ext, file_decompressed, dec);
task->progress = file_archive_parse_file_progress(&dec->zlib);
task->progress = file_archive_parse_file_progress(&dec->archive);
if (task->cancelled || ret != 0)
{
task->error = dec->callback_error;
file_archive_parse_file_iterate_stop(&dec->zlib);
file_archive_parse_file_iterate_stop(&dec->archive);
task_decompress_handler_finished(task, dec);
}
@ -178,16 +178,16 @@ static void task_decompress_handler_target_file(retro_task_t *task)
{
bool retdec;
decompress_state_t *dec = (decompress_state_t*)task->state;
int ret = file_archive_parse_file_iterate(&dec->zlib,
int ret = file_archive_parse_file_iterate(&dec->archive,
&retdec, dec->source_file,
dec->valid_ext, file_decompressed_target_file, dec);
task->progress = file_archive_parse_file_progress(&dec->zlib);
task->progress = file_archive_parse_file_progress(&dec->archive);
if (task->cancelled || ret != 0)
{
task->error = dec->callback_error;
file_archive_parse_file_iterate_stop(&dec->zlib);
file_archive_parse_file_iterate_stop(&dec->archive);
task_decompress_handler_finished(task, dec);
}
@ -197,16 +197,16 @@ static void task_decompress_handler_subdir(retro_task_t *task)
{
bool retdec;
decompress_state_t *dec = (decompress_state_t*)task->state;
int ret = file_archive_parse_file_iterate(&dec->zlib,
int ret = file_archive_parse_file_iterate(&dec->archive,
&retdec, dec->source_file,
dec->valid_ext, file_decompressed_subdir, dec);
task->progress = file_archive_parse_file_progress(&dec->zlib);
task->progress = file_archive_parse_file_progress(&dec->archive);
if (task->cancelled || ret != 0)
{
task->error = dec->callback_error;
file_archive_parse_file_iterate_stop(&dec->zlib);
file_archive_parse_file_iterate_stop(&dec->archive);
task_decompress_handler_finished(task, dec);
}
@ -286,7 +286,7 @@ bool task_push_decompress(
s->target_dir = strdup(target_dir);
s->valid_ext = valid_ext ? strdup(valid_ext) : NULL;
s->zlib.type = ZLIB_TRANSFER_INIT;
s->archive.type = ARCHIVE_TRANSFER_INIT;
t = (retro_task_t*)calloc(1, sizeof(*t));