mirror of
https://github.com/libretro/RetroArch
synced 2025-04-10 06:44:27 +00:00
create 7z archive backend, enables scanning of 7z content
This commit is contained in:
parent
42cba656a1
commit
7c29fd6c33
@ -989,14 +989,13 @@ ifeq ($(HAVE_7ZIP),1)
|
|||||||
deps/7zip/7zCrc.o \
|
deps/7zip/7zCrc.o \
|
||||||
deps/7zip/Lzma2Dec.o \
|
deps/7zip/Lzma2Dec.o \
|
||||||
deps/7zip/7zBuf.o
|
deps/7zip/7zBuf.o
|
||||||
OBJ += $(7ZOBJ)
|
OBJ += libretro-common/file/archive_file_7z.o \
|
||||||
|
$(7ZOBJ)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
||||||
ifeq ($(HAVE_ZLIB), 1)
|
ifeq ($(HAVE_ZLIB), 1)
|
||||||
OBJ += libretro-common/file/archive_file.o \
|
OBJ += libretro-common/file/archive_file_zlib.o
|
||||||
libretro-common/file/archive_file_zlib.o \
|
|
||||||
tasks/task_decompress.o
|
|
||||||
OBJ += $(ZLIB_OBJS)
|
OBJ += $(ZLIB_OBJS)
|
||||||
DEFINES += -DHAVE_ZLIB
|
DEFINES += -DHAVE_ZLIB
|
||||||
HAVE_COMPRESSION = 1
|
HAVE_COMPRESSION = 1
|
||||||
@ -1147,6 +1146,8 @@ endif
|
|||||||
|
|
||||||
ifeq ($(HAVE_COMPRESSION), 1)
|
ifeq ($(HAVE_COMPRESSION), 1)
|
||||||
DEFINES += -DHAVE_COMPRESSION
|
DEFINES += -DHAVE_COMPRESSION
|
||||||
|
OBJ += libretro-common/file/archive_file.o \
|
||||||
|
tasks/task_decompress.o
|
||||||
endif
|
endif
|
||||||
|
|
||||||
#ifeq ($(HAVE_DIRECTX), 1)
|
#ifeq ($(HAVE_DIRECTX), 1)
|
||||||
|
@ -46,6 +46,7 @@ else
|
|||||||
CFLAGS += -DHAVE_COMPRESSION
|
CFLAGS += -DHAVE_COMPRESSION
|
||||||
OBJS += libretro-common/file/archive_file.o
|
OBJS += libretro-common/file/archive_file.o
|
||||||
OBJS += libretro-common/file/archive_file_zlib.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 += libretro-common/encodings/encoding_utf.o
|
||||||
OBJS += verbosity.o
|
OBJS += verbosity.o
|
||||||
OBJS += performance.o
|
OBJS += performance.o
|
||||||
|
@ -872,7 +872,7 @@ static char buildbot_assets_server_url[] = "http://buildbot.libretro.com/assets/
|
|||||||
|
|
||||||
/* User 1 */
|
/* User 1 */
|
||||||
static const struct retro_keybind retro_keybinds_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_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_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 },
|
{ 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 */
|
/* Users 2 to MAX_USERS */
|
||||||
static const struct retro_keybind retro_keybinds_rest[] = {
|
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_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_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 },
|
{ true, RETRO_DEVICE_ID_JOYPAD_SELECT, RETRO_LBL_JOYPAD_SELECT, RETROK_UNKNOWN, NO_BTN, 0, AXIS_NONE },
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
* Copyright (C) 2010-2014 - Hans-Kristian Arntzen
|
* Copyright (C) 2010-2014 - Hans-Kristian Arntzen
|
||||||
* Copyright (C) 2011-2016 - Daniel De Matteis
|
* Copyright (C) 2011-2016 - Daniel De Matteis
|
||||||
* Copyright (C) 2013-2015 - Jason Fetters
|
* Copyright (C) 2013-2015 - Jason Fetters
|
||||||
*
|
*
|
||||||
* RetroArch is free software: you can redistribute it and/or modify it under the terms
|
* 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-
|
* 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.
|
* 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_NONE = 0,
|
||||||
DATABASE_TYPE_ITERATE,
|
DATABASE_TYPE_ITERATE,
|
||||||
DATABASE_TYPE_ITERATE_ZIP,
|
DATABASE_TYPE_ITERATE_ARCHIVE,
|
||||||
DATABASE_TYPE_ITERATE_LUTRO,
|
DATABASE_TYPE_ITERATE_LUTRO,
|
||||||
DATABASE_TYPE_SERIAL_LOOKUP,
|
DATABASE_TYPE_SERIAL_LOOKUP,
|
||||||
DATABASE_TYPE_CRC_LOOKUP
|
DATABASE_TYPE_CRC_LOOKUP
|
||||||
@ -58,7 +58,7 @@ typedef struct
|
|||||||
enum database_type type;
|
enum database_type type;
|
||||||
size_t list_ptr;
|
size_t list_ptr;
|
||||||
struct string_list *list;
|
struct string_list *list;
|
||||||
#ifdef HAVE_ZLIB
|
#ifdef HAVE_COMPRESSION
|
||||||
file_archive_transfer_t state;
|
file_archive_transfer_t state;
|
||||||
#endif
|
#endif
|
||||||
} database_info_handle_t;
|
} database_info_handle_t;
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
@ -40,7 +41,7 @@ static INLINE unsigned leading_ones(uint8_t c)
|
|||||||
return ones;
|
return ones;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Simple implementation. Assumes the sequence is
|
/* Simple implementation. Assumes the sequence is
|
||||||
* properly synchronized and terminated. */
|
* properly synchronized and terminated. */
|
||||||
|
|
||||||
size_t utf8_conv_utf32(uint32_t *out, size_t out_chars,
|
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)))
|
if (value < (((uint32_t)1) << (numAdds * 5 + 6)))
|
||||||
break;
|
break;
|
||||||
if (out)
|
if (out)
|
||||||
out[out_pos] = (char)(kUtf8Limits[numAdds - 1]
|
out[out_pos] = (char)(kUtf8Limits[numAdds - 1]
|
||||||
+ (value >> (6 * numAdds)));
|
+ (value >> (6 * numAdds)));
|
||||||
out_pos++;
|
out_pos++;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
numAdds--;
|
numAdds--;
|
||||||
if (out)
|
if (out)
|
||||||
out[out_pos] = (char)(0x80
|
out[out_pos] = (char)(0x80
|
||||||
+ ((value >> (6 * numAdds)) & 0x3F));
|
+ ((value >> (6 * numAdds)) & 0x3F));
|
||||||
out_pos++;
|
out_pos++;
|
||||||
}while (numAdds != 0);
|
}while (numAdds != 0);
|
||||||
@ -136,13 +137,13 @@ bool utf16_conv_utf8(uint8_t *out, size_t *out_chars,
|
|||||||
return false;
|
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.
|
* but at most d_len bytes.
|
||||||
*
|
*
|
||||||
* Always NULL terminates.
|
* Always NULL terminates.
|
||||||
* Does not copy half a character.
|
* Does not copy half a character.
|
||||||
*
|
*
|
||||||
* Returns number of bytes. 's' is assumed valid UTF-8.
|
* Returns number of bytes. 's' is assumed valid UTF-8.
|
||||||
* Use only if 'chars' is considerably less than 'd_len'. */
|
* 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);
|
uint8_t first = utf8_walkbyte(string);
|
||||||
uint32_t ret;
|
uint32_t ret;
|
||||||
|
|
||||||
if (first<128)
|
if (first<128)
|
||||||
return first;
|
return first;
|
||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
ret = (ret<<6) | (utf8_walkbyte(string) & 0x3F);
|
ret = (ret<<6) | (utf8_walkbyte(string) & 0x3F);
|
||||||
if (first >= 0xE0)
|
if (first >= 0xE0)
|
||||||
ret = (ret<<6) | (utf8_walkbyte(string) & 0x3F);
|
ret = (ret<<6) | (utf8_walkbyte(string) & 0x3F);
|
||||||
if (first >= 0xF0)
|
if (first >= 0xF0)
|
||||||
ret = (ret<<6) | (utf8_walkbyte(string) & 0x3F);
|
ret = (ret<<6) | (utf8_walkbyte(string) & 0x3F);
|
||||||
|
|
||||||
if (first >= 0xF0)
|
if (first >= 0xF0)
|
||||||
return ret | (first&31)<<18;
|
return ret | (first&31)<<18;
|
||||||
if (first >= 0xE0)
|
if (first >= 0xE0)
|
||||||
return ret | (first&15)<<12;
|
return ret | (first&15)<<12;
|
||||||
return ret | (first&7)<<6;
|
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;
|
||||||
|
}
|
||||||
|
@ -45,30 +45,7 @@
|
|||||||
#include <retro_stat.h>
|
#include <retro_stat.h>
|
||||||
#include <retro_miscellaneous.h>
|
#include <retro_miscellaneous.h>
|
||||||
#include <lists/string_list.h>
|
#include <lists/string_list.h>
|
||||||
|
#include <string/stdstring.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
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
@ -81,7 +58,7 @@ typedef struct
|
|||||||
|
|
||||||
#ifdef HAVE_MMAP
|
#ifdef HAVE_MMAP
|
||||||
/* Closes, unmaps and frees. */
|
/* 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;
|
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);
|
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;
|
file_archive_file_data_t *data = (file_archive_file_data_t*)handle;
|
||||||
if (!data)
|
if (!data)
|
||||||
@ -146,16 +123,17 @@ error:
|
|||||||
#else
|
#else
|
||||||
|
|
||||||
/* Closes, unmaps and frees. */
|
/* 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;
|
file_archive_file_data_t *data = (file_archive_file_data_t*)handle;
|
||||||
if (!data)
|
if (!data)
|
||||||
return;
|
return;
|
||||||
free(data->data);
|
if(data->data)
|
||||||
|
free(data->data);
|
||||||
free(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;
|
file_archive_file_data_t *data = (file_archive_file_data_t*)handle;
|
||||||
if (!data)
|
if (!data)
|
||||||
@ -209,7 +187,9 @@ static int file_archive_get_file_list_cb(
|
|||||||
union string_list_elem_attr attr;
|
union string_list_elem_attr attr;
|
||||||
struct string_list *ext_list = NULL;
|
struct string_list *ext_list = NULL;
|
||||||
const char *file_ext = 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)cdata;
|
||||||
(void)cmode;
|
(void)cmode;
|
||||||
@ -219,13 +199,16 @@ static int file_archive_get_file_list_cb(
|
|||||||
|
|
||||||
memset(&attr, 0, sizeof(attr));
|
memset(&attr, 0, sizeof(attr));
|
||||||
|
|
||||||
|
if (!pathLen)
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (valid_exts)
|
if (valid_exts)
|
||||||
ext_list = string_split(valid_exts, "|");
|
ext_list = string_split(valid_exts, "|");
|
||||||
|
|
||||||
if (ext_list)
|
if (ext_list)
|
||||||
{
|
{
|
||||||
/* Checks if this entry is a directory or a file. */
|
/* 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. */
|
/* Skip if directory. */
|
||||||
if (last_char == '/' || last_char == '\\' )
|
if (last_char == '/' || last_char == '\\' )
|
||||||
@ -233,7 +216,7 @@ static int file_archive_get_file_list_cb(
|
|||||||
|
|
||||||
file_ext = path_get_extension(path);
|
file_ext = path_get_extension(path);
|
||||||
|
|
||||||
if (!file_ext ||
|
if (!file_ext ||
|
||||||
!string_list_find_elem_prefix(ext_list, ".", file_ext))
|
!string_list_find_elem_prefix(ext_list, ".", file_ext))
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
@ -241,8 +224,8 @@ static int file_archive_get_file_list_cb(
|
|||||||
string_list_free(ext_list);
|
string_list_free(ext_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
return string_list_append(list, path, attr);
|
return string_list_append(data->list, path, attr);
|
||||||
|
|
||||||
error:
|
error:
|
||||||
string_list_free(ext_list);
|
string_list_free(ext_list);
|
||||||
return 0;
|
return 0;
|
||||||
@ -254,9 +237,9 @@ static int file_archive_extract_cb(const char *name, const char *valid_exts,
|
|||||||
uint32_t checksum, void *userdata)
|
uint32_t checksum, void *userdata)
|
||||||
{
|
{
|
||||||
const char *ext = path_get_extension(name);
|
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))
|
if (ext && string_list_find_elem(data->ext, ext))
|
||||||
{
|
{
|
||||||
char new_path[PATH_MAX_LENGTH] = {0};
|
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,
|
fill_pathname_join(new_path, data->extraction_directory,
|
||||||
path_basename(name), sizeof(new_path));
|
path_basename(name), sizeof(new_path));
|
||||||
else
|
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));
|
path_basename(name), sizeof(new_path));
|
||||||
|
|
||||||
data->first_extracted_file_path = strdup(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,
|
valid_exts, cdata, cmode, csize, size,
|
||||||
0, NULL);
|
0, data);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t read_le(const uint8_t *data, unsigned size)
|
int file_archive_parse_file_init(file_archive_transfer_t *state,
|
||||||
{
|
|
||||||
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,
|
|
||||||
const char *file)
|
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)
|
if (!state->backend)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
state->handle = file_archive_open(file);
|
state->handle = file_archive_open(path);
|
||||||
if (!state->handle)
|
if (!state->handle)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
state->zip_size = file_archive_size(state->handle);
|
state->archive_size = file_archive_size(state->handle);
|
||||||
if (state->zip_size < 22)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
state->data = file_archive_data(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--)
|
return state->backend->archive_parse_file_init(state, path);
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* file_archive_decompress_data_to_file:
|
* file_archive_decompress_data_to_file:
|
||||||
* @path : filename path of archive.
|
* @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.
|
* If NULL, allow all.
|
||||||
* @cdata : input data.
|
* @cdata : input data.
|
||||||
* @csize : size of input data.
|
* @csize : size of input data.
|
||||||
@ -420,12 +313,6 @@ static int file_archive_decompress_data_to_file(
|
|||||||
uint32_t size,
|
uint32_t size,
|
||||||
uint32_t checksum)
|
uint32_t checksum)
|
||||||
{
|
{
|
||||||
if (handle)
|
|
||||||
{
|
|
||||||
handle->backend->stream_free(handle->stream);
|
|
||||||
free(handle->stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!handle || ret == -1)
|
if (!handle || ret == -1)
|
||||||
{
|
{
|
||||||
ret = 0;
|
ret = 0;
|
||||||
@ -434,12 +321,13 @@ static int file_archive_decompress_data_to_file(
|
|||||||
|
|
||||||
handle->real_checksum = handle->backend->stream_crc_calculate(
|
handle->real_checksum = handle->backend->stream_crc_calculate(
|
||||||
0, handle->data, size);
|
0, handle->data, size);
|
||||||
|
handle->backend->stream_free(handle->stream);
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
if (handle->real_checksum != checksum)
|
if (handle->real_checksum != checksum)
|
||||||
{
|
{
|
||||||
/* File CRC difers from ZIP CRC. */
|
/* File CRC difers from archive CRC. */
|
||||||
printf("File CRC differs from ZIP CRC. File: 0x%x, ZIP: 0x%x.\n",
|
printf("File CRC differs from archive CRC. File: 0x%x, Archive: 0x%x.\n",
|
||||||
(unsigned)handle->real_checksum, (unsigned)checksum);
|
(unsigned)handle->real_checksum, (unsigned)checksum);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -456,6 +344,15 @@ end:
|
|||||||
return ret;
|
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(
|
int file_archive_parse_file_iterate(
|
||||||
file_archive_transfer_t *state,
|
file_archive_transfer_t *state,
|
||||||
bool *returnerr,
|
bool *returnerr,
|
||||||
@ -469,58 +366,81 @@ int file_archive_parse_file_iterate(
|
|||||||
|
|
||||||
switch (state->type)
|
switch (state->type)
|
||||||
{
|
{
|
||||||
case ZLIB_TRANSFER_NONE:
|
case ARCHIVE_TRANSFER_NONE:
|
||||||
break;
|
break;
|
||||||
case ZLIB_TRANSFER_INIT:
|
case ARCHIVE_TRANSFER_INIT:
|
||||||
if (file_archive_parse_file_init(state, file) == 0)
|
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,
|
struct archive_extract_userdata *data =
|
||||||
valid_exts, userdata, file_cb);
|
(struct archive_extract_userdata*)userdata;
|
||||||
if (ret != 1)
|
if (data)
|
||||||
state->type = ZLIB_TRANSFER_DEINIT;
|
data->context = state->stream;
|
||||||
if (ret == -1)
|
state->type = ARCHIVE_TRANSFER_ITERATE;
|
||||||
state->type = ZLIB_TRANSFER_DEINIT_ERROR;
|
}
|
||||||
|
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;
|
break;
|
||||||
case ZLIB_TRANSFER_DEINIT_ERROR:
|
case ARCHIVE_TRANSFER_DEINIT_ERROR:
|
||||||
*returnerr = false;
|
*returnerr = false;
|
||||||
case ZLIB_TRANSFER_DEINIT:
|
case ARCHIVE_TRANSFER_DEINIT:
|
||||||
if (state->handle)
|
if (state->handle)
|
||||||
|
{
|
||||||
file_archive_free(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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state->type == ZLIB_TRANSFER_DEINIT ||
|
if (state->type == ARCHIVE_TRANSFER_DEINIT ||
|
||||||
state->type == ZLIB_TRANSFER_DEINIT_ERROR)
|
state->type == ARCHIVE_TRANSFER_DEINIT_ERROR)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
return 0;
|
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_archive_parse_file:
|
||||||
* @file : filename path of archive
|
* @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.
|
* If NULL, allow all.
|
||||||
* @file_cb : file_cb function pointer
|
* @file_cb : file_cb function pointer
|
||||||
* @userdata : userdata to pass to 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.
|
* file_cb with userdata.
|
||||||
*
|
*
|
||||||
* Returns: true (1) on success, otherwise false (0).
|
* 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};
|
file_archive_transfer_t state = {0};
|
||||||
bool returnerr = true;
|
bool returnerr = true;
|
||||||
|
|
||||||
state.type = ZLIB_TRANSFER_INIT;
|
state.type = ARCHIVE_TRANSFER_INIT;
|
||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
@ -547,33 +467,33 @@ int file_archive_parse_file_progress(file_archive_transfer_t *state)
|
|||||||
{
|
{
|
||||||
/* FIXME: this estimate is worse than before */
|
/* FIXME: this estimate is worse than before */
|
||||||
ptrdiff_t delta = state->directory - state->data;
|
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:
|
* file_archive_extract_first_file:
|
||||||
* @zip_path : filename path to ZIP archive.
|
* @archive_path : filename path to archive.
|
||||||
* @zip_path_size : size of ZIP archive.
|
* @archive_path_size : size of archive.
|
||||||
* @valid_exts : valid extensions for a content file.
|
* @valid_exts : valid extensions for the file.
|
||||||
* @extraction_directory : the directory to extract temporary
|
* @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).
|
* Returns : true (1) on success, otherwise false (0).
|
||||||
**/
|
**/
|
||||||
bool file_archive_extract_first_content_file(
|
bool file_archive_extract_first_file(
|
||||||
char *zip_path,
|
char *archive_path,
|
||||||
size_t zip_path_size,
|
size_t archive_path_size,
|
||||||
const char *valid_exts,
|
const char *valid_exts,
|
||||||
const char *extraction_directory,
|
const char *extraction_directory,
|
||||||
char *out_path, size_t len)
|
char *out_path, size_t len)
|
||||||
{
|
{
|
||||||
struct string_list *list = NULL;
|
struct string_list *list = NULL;
|
||||||
bool ret = true;
|
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. */
|
* implementation does not have any valid extensions. */
|
||||||
if (!valid_exts)
|
if (!valid_exts)
|
||||||
return false;
|
return false;
|
||||||
@ -585,12 +505,14 @@ bool file_archive_extract_first_content_file(
|
|||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
userdata.zip_path = zip_path;
|
userdata.archive_path = archive_path;
|
||||||
userdata.zip_path_size = zip_path_size;
|
userdata.archive_path_size = archive_path_size;
|
||||||
userdata.extraction_directory = extraction_directory;
|
userdata.extraction_directory = extraction_directory;
|
||||||
userdata.ext = list;
|
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))
|
file_archive_extract_cb, &userdata))
|
||||||
{
|
{
|
||||||
/* Parsing file archive failed. */
|
/* Parsing file archive failed. */
|
||||||
@ -598,9 +520,9 @@ bool file_archive_extract_first_content_file(
|
|||||||
goto end;
|
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. */
|
* for libretro implementation. */
|
||||||
ret = false;
|
ret = false;
|
||||||
goto end;
|
goto end;
|
||||||
@ -626,20 +548,22 @@ end:
|
|||||||
struct string_list *file_archive_get_file_list(const char *path,
|
struct string_list *file_archive_get_file_list(const char *path,
|
||||||
const char *valid_exts)
|
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;
|
goto error;
|
||||||
|
|
||||||
if (!file_archive_parse_file(path, valid_exts,
|
if (!file_archive_parse_file(path, valid_exts,
|
||||||
file_archive_get_file_list_cb, list))
|
file_archive_get_file_list_cb, &userdata))
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
return list;
|
return userdata.list;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
if (list)
|
if (userdata.list)
|
||||||
string_list_free(list);
|
string_list_free(userdata.list);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -649,16 +573,19 @@ bool file_archive_perform_mode(const char *path, const char *valid_exts,
|
|||||||
{
|
{
|
||||||
switch (cmode)
|
switch (cmode)
|
||||||
{
|
{
|
||||||
case ZLIB_MODE_UNCOMPRESSED:
|
case ARCHIVE_MODE_UNCOMPRESSED:
|
||||||
if (!filestream_write_file(path, cdata, size))
|
if (!filestream_write_file(path, cdata, size))
|
||||||
goto error;
|
goto error;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ZLIB_MODE_DEFLATE:
|
case ARCHIVE_MODE_COMPRESSED:
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
file_archive_file_handle_t handle = {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,
|
if (!handle.backend->stream_decompress_data_to_file_init(&handle,
|
||||||
cdata, csize, size))
|
cdata, csize, size))
|
||||||
@ -685,7 +612,154 @@ error:
|
|||||||
return false;
|
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;
|
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;
|
||||||
|
}
|
||||||
|
405
libretro-common/file/archive_file_7z.c
Normal file
405
libretro-common/file/archive_file_7z.c
Normal 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"
|
||||||
|
};
|
@ -25,8 +25,18 @@
|
|||||||
#include <compat/zlib.h>
|
#include <compat/zlib.h>
|
||||||
#include <file/archive_file.h>
|
#include <file/archive_file.h>
|
||||||
#include <streams/file_stream.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));
|
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()))
|
if (!(handle->stream = (z_stream*)zlib_stream_new()))
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
if (inflateInit2((z_streamp)handle->stream, -MAX_WBITS) != Z_OK)
|
if (inflateInit2((z_streamp)handle->stream, -MAX_WBITS) != Z_OK)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
@ -197,6 +207,265 @@ static uint32_t zlib_stream_crc32_calculate(uint32_t crc,
|
|||||||
return crc32(crc, data, length);
|
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 = {
|
const struct file_archive_file_backend zlib_backend = {
|
||||||
zlib_stream_new,
|
zlib_stream_new,
|
||||||
zlib_stream_free,
|
zlib_stream_free,
|
||||||
@ -212,5 +481,8 @@ const struct file_archive_file_backend zlib_backend = {
|
|||||||
zlib_stream_compress_free,
|
zlib_stream_compress_free,
|
||||||
zlib_stream_compress_data_to_file,
|
zlib_stream_compress_data_to_file,
|
||||||
zlib_stream_crc32_calculate,
|
zlib_stream_crc32_calculate,
|
||||||
|
zip_file_read,
|
||||||
|
zip_parse_file_init,
|
||||||
|
zip_parse_file_iterate_step,
|
||||||
"zlib"
|
"zlib"
|
||||||
};
|
};
|
||||||
|
@ -194,12 +194,12 @@ bool path_is_compressed_file(const char* path)
|
|||||||
const char *ext = path_get_extension(path);
|
const char *ext = path_get_extension(path);
|
||||||
|
|
||||||
#ifdef HAVE_ZLIB
|
#ifdef HAVE_ZLIB
|
||||||
if (strcasestr(ext, "zip"))
|
if (string_is_equal_noncase(ext, "zip"))
|
||||||
return true;
|
return true;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_7ZIP
|
#ifdef HAVE_7ZIP
|
||||||
if (strcasestr(ext, "7z"))
|
if (string_is_equal_noncase(ext, "7z"))
|
||||||
return true;
|
return true;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -870,7 +870,7 @@ static struct rpng_process *rpng_process_init(rpng_t *rpng, unsigned *width, uns
|
|||||||
if (!process)
|
if (!process)
|
||||||
return NULL;
|
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,
|
png_pass_geom(&rpng->ihdr, rpng->ihdr.width,
|
||||||
rpng->ihdr.height, NULL, NULL, &process->inflate_buf_size);
|
rpng->ihdr.height, NULL, NULL, &process->inflate_buf_size);
|
||||||
|
@ -48,7 +48,7 @@ static bool png_write_crc(RFILE *file, const uint8_t *data, size_t size)
|
|||||||
{
|
{
|
||||||
uint8_t crc_raw[4] = {0};
|
uint8_t crc_raw[4] = {0};
|
||||||
const struct file_archive_file_backend *stream_backend =
|
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);
|
uint32_t crc = stream_backend->stream_crc_calculate(0, data, size);
|
||||||
|
|
||||||
dword_write_be(crc_raw, crc);
|
dword_write_be(crc_raw, crc);
|
||||||
@ -230,7 +230,7 @@ static bool rpng_save_image(const char *path,
|
|||||||
if (!file)
|
if (!file)
|
||||||
GOTO_END_ERROR();
|
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))
|
if (filestream_write(file, png_magic, sizeof(png_magic)) != sizeof(png_magic))
|
||||||
GOTO_END_ERROR();
|
GOTO_END_ERROR();
|
||||||
|
@ -42,4 +42,9 @@ const char *utf8skip(const char *str, size_t chars);
|
|||||||
|
|
||||||
uint32_t utf8_walk(const char **string);
|
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
|
#endif
|
||||||
|
@ -30,21 +30,56 @@
|
|||||||
|
|
||||||
enum file_archive_transfer_type
|
enum file_archive_transfer_type
|
||||||
{
|
{
|
||||||
ZLIB_TRANSFER_NONE = 0,
|
ARCHIVE_TRANSFER_NONE = 0,
|
||||||
ZLIB_TRANSFER_INIT,
|
ARCHIVE_TRANSFER_INIT,
|
||||||
ZLIB_TRANSFER_ITERATE,
|
ARCHIVE_TRANSFER_ITERATE,
|
||||||
ZLIB_TRANSFER_DEINIT,
|
ARCHIVE_TRANSFER_DEINIT,
|
||||||
ZLIB_TRANSFER_DEINIT_ERROR
|
ARCHIVE_TRANSFER_DEINIT_ERROR
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct file_archive_handle
|
typedef struct file_archive_handle
|
||||||
{
|
{
|
||||||
void *stream;
|
void *stream;
|
||||||
uint8_t *data;
|
uint8_t *data;
|
||||||
uint32_t real_checksum;
|
uint32_t real_checksum;
|
||||||
const struct file_archive_file_backend *backend;
|
const struct file_archive_file_backend *backend;
|
||||||
} file_archive_file_handle_t;
|
} 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
|
struct file_archive_file_backend
|
||||||
{
|
{
|
||||||
void *(*stream_new)(void);
|
void *(*stream_new)(void);
|
||||||
@ -63,26 +98,19 @@ struct file_archive_file_backend
|
|||||||
void (*stream_compress_free)(void *);
|
void (*stream_compress_free)(void *);
|
||||||
int (*stream_compress_data_to_file)(void *);
|
int (*stream_compress_data_to_file)(void *);
|
||||||
uint32_t (*stream_crc_calculate)(uint32_t, const uint8_t *, size_t);
|
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;
|
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(
|
int file_archive_parse_file_iterate(
|
||||||
file_archive_transfer_t *state,
|
file_archive_transfer_t *state,
|
||||||
bool *returnerr,
|
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);
|
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 : filename path to ZIP archive.
|
||||||
* @zip_path_size : size of 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
|
* @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).
|
* 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,
|
const char *valid_exts, const char *extraction_dir,
|
||||||
char *out_path, size_t len);
|
char *out_path, size_t len);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* file_archive_get_file_list:
|
* file_archive_get_file_list:
|
||||||
* @path : filename path of archive
|
* @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.
|
* If NULL, allow all.
|
||||||
*
|
*
|
||||||
* Returns: string listing of files from archive on success, otherwise NULL.
|
* 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,
|
bool file_archive_perform_mode(const char *name, const char *valid_exts,
|
||||||
const uint8_t *cdata, unsigned cmode, uint32_t csize, uint32_t size,
|
const uint8_t *cdata, unsigned cmode, uint32_t csize, uint32_t size,
|
||||||
uint32_t crc32, void *userdata);
|
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);
|
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 zlib_backend;
|
||||||
|
extern const struct file_archive_file_backend sevenzip_backend;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ struct string_list_elem
|
|||||||
union string_list_elem_attr attr;
|
union string_list_elem_attr attr;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct string_list
|
struct __attribute__ ((aligned(1))) string_list
|
||||||
{
|
{
|
||||||
struct string_list_elem *elems;
|
struct string_list_elem *elems;
|
||||||
size_t size;
|
size_t size;
|
||||||
@ -138,7 +138,7 @@ void string_list_free(struct string_list *list);
|
|||||||
* @list : pointer to string list.
|
* @list : pointer to string list.
|
||||||
* @delim : delimiter character for @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.
|
* string to @buffer, delimited by @delim.
|
||||||
*/
|
*/
|
||||||
void string_list_join_concat(char *buffer, size_t size,
|
void string_list_join_concat(char *buffer, size_t size,
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
#elif defined(GEKKO) || defined(__PSL1GHT__) || defined(__QNX__)
|
#elif defined(GEKKO) || defined(__PSL1GHT__) || defined(__QNX__)
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#elif defined(PSP)
|
#elif defined(PSP)
|
||||||
#include <pspthreadman.h>
|
#include <pspthreadman.h>
|
||||||
#elif defined(VITA)
|
#elif defined(VITA)
|
||||||
#include <psp2/kernel/threadmgr.h>
|
#include <psp2/kernel/threadmgr.h>
|
||||||
#elif defined(_3DS)
|
#elif defined(_3DS)
|
||||||
@ -159,6 +159,18 @@ static INLINE float db_to_gain(float db)
|
|||||||
return powf(10.0f, db / 20.0f);
|
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.
|
/* Helper macros and struct to keep track of many booleans.
|
||||||
* To check for multiple bits, use &&, not &.
|
* To check for multiple bits, use &&, not &.
|
||||||
* For OR, | can be used. */
|
* For OR, | can be used. */
|
||||||
|
@ -186,7 +186,7 @@ void string_list_set(struct string_list *list,
|
|||||||
* @list : pointer to string list.
|
* @list : pointer to string list.
|
||||||
* @delim : delimiter character for @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.
|
* string to @buffer, delimited by @delim.
|
||||||
*/
|
*/
|
||||||
void string_list_join_concat(char *buffer, size_t size,
|
void string_list_join_concat(char *buffer, size_t size,
|
||||||
|
@ -3372,7 +3372,9 @@ static int menu_displaylist_parse_generic(
|
|||||||
filter_ext = true;
|
filter_ext = true;
|
||||||
|
|
||||||
if (path_is_compressed)
|
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
|
else
|
||||||
str_list = dir_list_new(info->path,
|
str_list = dir_list_new(info->path,
|
||||||
filter_ext ? info->exts : NULL,
|
filter_ext ? info->exts : NULL,
|
||||||
|
@ -198,6 +198,8 @@ uint32_t msg_hash_calculate(const char *s)
|
|||||||
|
|
||||||
#define FILE_HASH_APK 0x0b885e61U
|
#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 0x0b88c7d8U
|
||||||
#define HASH_EXTENSION_ZIP_UPP 0x0b883b78U
|
#define HASH_EXTENSION_ZIP_UPP 0x0b883b78U
|
||||||
#define HASH_EXTENSION_CUE 0x0b886782U
|
#define HASH_EXTENSION_CUE 0x0b886782U
|
||||||
@ -211,6 +213,8 @@ enum msg_file_type msg_hash_to_file_type(uint32_t hash)
|
|||||||
switch (hash)
|
switch (hash)
|
||||||
{
|
{
|
||||||
case MENU_VALUE_COMP:
|
case MENU_VALUE_COMP:
|
||||||
|
case HASH_EXTENSION_7Z:
|
||||||
|
case HASH_EXTENSION_7Z_UPP:
|
||||||
case HASH_EXTENSION_ZIP:
|
case HASH_EXTENSION_ZIP:
|
||||||
case HASH_EXTENSION_ZIP_UPP:
|
case HASH_EXTENSION_ZIP_UPP:
|
||||||
case FILE_HASH_APK:
|
case FILE_HASH_APK:
|
||||||
|
12
patch.c
12
patch.c
@ -90,7 +90,7 @@ static uint8_t bps_read(struct bps_data *bps)
|
|||||||
uint8_t data = bps->modify_data[bps->modify_offset++];
|
uint8_t data = bps->modify_data[bps->modify_offset++];
|
||||||
#ifdef HAVE_ZLIB
|
#ifdef HAVE_ZLIB
|
||||||
const struct file_archive_file_backend *stream_backend =
|
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 = ~stream_backend->stream_crc_calculate(
|
||||||
~bps->modify_checksum, &data, 1);
|
~bps->modify_checksum, &data, 1);
|
||||||
#endif
|
#endif
|
||||||
@ -118,7 +118,7 @@ static void bps_write(struct bps_data *bps, uint8_t data)
|
|||||||
{
|
{
|
||||||
#ifdef HAVE_ZLIB
|
#ifdef HAVE_ZLIB
|
||||||
const struct file_archive_file_backend *stream_backend =
|
const struct file_archive_file_backend *stream_backend =
|
||||||
file_archive_get_default_file_backend();
|
file_archive_get_zlib_file_backend();
|
||||||
#endif
|
#endif
|
||||||
if (!bps)
|
if (!bps)
|
||||||
return;
|
return;
|
||||||
@ -142,7 +142,7 @@ static enum patch_error bps_apply_patch(
|
|||||||
modify_modify_checksum = 0, checksum;
|
modify_modify_checksum = 0, checksum;
|
||||||
#ifdef HAVE_ZLIB
|
#ifdef HAVE_ZLIB
|
||||||
const struct file_archive_file_backend *stream_backend =
|
const struct file_archive_file_backend *stream_backend =
|
||||||
file_archive_get_default_file_backend();
|
file_archive_get_zlib_file_backend();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (modify_length < 19)
|
if (modify_length < 19)
|
||||||
@ -254,7 +254,7 @@ static uint8_t ups_patch_read(struct ups_data *data)
|
|||||||
{
|
{
|
||||||
#ifdef HAVE_ZLIB
|
#ifdef HAVE_ZLIB
|
||||||
const struct file_archive_file_backend *stream_backend =
|
const struct file_archive_file_backend *stream_backend =
|
||||||
file_archive_get_default_file_backend();
|
file_archive_get_zlib_file_backend();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (data && data->patch_offset < data->patch_length)
|
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
|
#ifdef HAVE_ZLIB
|
||||||
const struct file_archive_file_backend *stream_backend =
|
const struct file_archive_file_backend *stream_backend =
|
||||||
file_archive_get_default_file_backend();
|
file_archive_get_zlib_file_backend();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (data && data->source_offset < data->source_length)
|
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
|
#ifdef HAVE_ZLIB
|
||||||
const struct file_archive_file_backend *stream_backend =
|
const struct file_archive_file_backend *stream_backend =
|
||||||
file_archive_get_default_file_backend();
|
file_archive_get_zlib_file_backend();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (data && data->target_offset < data->target_length)
|
if (data && data->target_offset < data->target_length)
|
||||||
|
@ -90,18 +90,10 @@
|
|||||||
#include "../paths.h"
|
#include "../paths.h"
|
||||||
#include "../verbosity.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
|
#ifdef HAVE_CHEEVOS
|
||||||
#include "../cheevos.h"
|
#include "../cheevos.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
typedef struct content_stream
|
typedef struct content_stream
|
||||||
{
|
{
|
||||||
uint32_t a;
|
uint32_t a;
|
||||||
@ -116,610 +108,6 @@ static bool _content_is_inited = false;
|
|||||||
static bool core_does_not_need_content = false;
|
static bool core_does_not_need_content = false;
|
||||||
static uint32_t content_crc = 0;
|
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);
|
|
||||||
|
|
||||||
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 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:
|
* content_file_read:
|
||||||
* @path : path to file.
|
* @path : path to file.
|
||||||
@ -727,7 +115,7 @@ error:
|
|||||||
* file into. Needs to be freed manually.
|
* file into. Needs to be freed manually.
|
||||||
* @length : Number of items read, -1 on error.
|
* @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().
|
* if path contains a compressed file, otherwise will call filestream_read_file().
|
||||||
*
|
*
|
||||||
* Returns: 1 if file read, 0 on error.
|
* Returns: 1 if file read, 0 on error.
|
||||||
@ -737,31 +125,13 @@ static int content_file_read(const char *path, void **buf, ssize_t *length)
|
|||||||
#ifdef HAVE_COMPRESSION
|
#ifdef HAVE_COMPRESSION
|
||||||
if (path_contains_compressed_file(path))
|
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;
|
return 1;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
return filestream_read_file(path, buf, length);
|
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)
|
static void check_defaults_dir_create_dir(const char *path)
|
||||||
{
|
{
|
||||||
char new_path[PATH_MAX_LENGTH] = {0};
|
char new_path[PATH_MAX_LENGTH] = {0};
|
||||||
@ -1049,7 +419,7 @@ static bool read_content_file(unsigned i, const char *path, void **buf,
|
|||||||
stream_info.c = *length;
|
stream_info.c = *length;
|
||||||
|
|
||||||
if (!stream_backend)
|
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.crc = stream_backend->stream_crc_calculate(
|
||||||
stream_info.a, stream_info.b, stream_info.c);
|
stream_info.a, stream_info.b, stream_info.c);
|
||||||
*content_crc_ptr = stream_info.crc;
|
*content_crc_ptr = stream_info.crc;
|
||||||
@ -1170,7 +540,7 @@ static bool load_content_from_compressed_archive(
|
|||||||
fill_pathname_join(new_path, new_basedir,
|
fill_pathname_join(new_path, new_basedir,
|
||||||
path_basename(path), sizeof(new_path));
|
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)
|
if (!ret || new_path_len < 0)
|
||||||
{
|
{
|
||||||
@ -1329,7 +699,7 @@ error:
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_ZLIB
|
#ifdef HAVE_COMPRESSION
|
||||||
static bool init_content_file_extract(
|
static bool init_content_file_extract(
|
||||||
struct string_list *temporary_content,
|
struct string_list *temporary_content,
|
||||||
struct string_list *content,
|
struct string_list *content,
|
||||||
@ -1343,50 +713,47 @@ static bool init_content_file_extract(
|
|||||||
|
|
||||||
for (i = 0; i < content->size; i++)
|
for (i = 0; i < content->size; i++)
|
||||||
{
|
{
|
||||||
const char *ext = NULL;
|
bool compressed = NULL;
|
||||||
const char *valid_ext = system->info.valid_extensions;
|
const char *valid_ext = system->info.valid_extensions;
|
||||||
|
|
||||||
/* Block extract check. */
|
/* Block extract check. */
|
||||||
if (content->elems[i].attr.i & 1)
|
if (content->elems[i].attr.i & 1)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
ext = path_get_extension(content->elems[i].data);
|
compressed = path_contains_compressed_file(content->elems[i].data);
|
||||||
|
|
||||||
if (special)
|
if (special)
|
||||||
valid_ext = special->roms[i].valid_extensions;
|
valid_ext = special->roms[i].valid_extensions;
|
||||||
|
|
||||||
if (!ext)
|
if (!compressed)
|
||||||
continue;
|
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};
|
RARCH_ERR("%s: %s.\n",
|
||||||
char temp_content[PATH_MAX_LENGTH] = {0};
|
msg_hash_to_str(
|
||||||
|
MSG_FAILED_TO_EXTRACT_CONTENT_FROM_COMPRESSED_FILE),
|
||||||
strlcpy(temp_content, content->elems[i].data,
|
temp_content);
|
||||||
sizeof(temp_content));
|
runloop_msg_queue_push(
|
||||||
|
msg_hash_to_str(MSG_FAILED_TO_EXTRACT_CONTENT_FROM_COMPRESSED_FILE)
|
||||||
if (!file_archive_extract_first_content_file(temp_content,
|
, 2, 180, true);
|
||||||
sizeof(temp_content), valid_ext,
|
return false;
|
||||||
*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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string_list_set(content, i, new_path);
|
||||||
|
if (!string_list_append(temporary_content,
|
||||||
|
new_path, *attr))
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -1438,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. */
|
/* Try to extract all content we're going to load if appropriate. */
|
||||||
if (!init_content_file_extract(temporary_content,
|
if (!init_content_file_extract(temporary_content,
|
||||||
content, system, special, &attr))
|
content, system, special, &attr))
|
||||||
@ -1583,7 +950,6 @@ error:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifdef HAVE_MENU
|
#ifdef HAVE_MENU
|
||||||
static void menu_content_environment_get(int *argc, char *argv[],
|
static void menu_content_environment_get(int *argc, char *argv[],
|
||||||
void *args, void *params_data)
|
void *args, void *params_data)
|
||||||
@ -1946,7 +1312,7 @@ bool task_push_content_load_default(
|
|||||||
/* Fork core? */
|
/* Fork core? */
|
||||||
switch (mode)
|
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))
|
if (!frontend_driver_set_fork(FRONTEND_FORK_CORE))
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
|
@ -47,9 +47,9 @@ typedef struct database_state_handle
|
|||||||
size_t list_index;
|
size_t list_index;
|
||||||
size_t entry_index;
|
size_t entry_index;
|
||||||
uint32_t crc;
|
uint32_t crc;
|
||||||
uint32_t zip_crc;
|
uint32_t archive_crc;
|
||||||
uint8_t *buf;
|
uint8_t *buf;
|
||||||
char zip_name[PATH_MAX_LENGTH];
|
char archive_name[PATH_MAX_LENGTH];
|
||||||
char serial[4096];
|
char serial[4096];
|
||||||
} database_state_handle_t;
|
} 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)
|
uint32_t crc32, void *userdata)
|
||||||
{
|
{
|
||||||
database_state_handle_t *db_state = (database_state_handle_t*)userdata;
|
database_state_handle_t *db_state = (database_state_handle_t*)userdata;
|
||||||
|
|
||||||
db_state->crc = crc32;
|
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
|
#if 0
|
||||||
RARCH_LOG("Going to compare CRC 0x%x for %s\n", crc32, name);
|
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};
|
char track_path[PATH_MAX_LENGTH] = {0};
|
||||||
int rv = find_first_data_track(name,
|
int rv = find_first_data_track(name,
|
||||||
&offset, track_path, PATH_MAX_LENGTH);
|
&offset, track_path, PATH_MAX_LENGTH);
|
||||||
|
|
||||||
if (rv < 0)
|
if (rv < 0)
|
||||||
{
|
{
|
||||||
RARCH_LOG("%s: %s\n",
|
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);
|
name, (void**)&db_state->buf, &ret);
|
||||||
|
|
||||||
#ifdef HAVE_ZLIB
|
#ifdef HAVE_ZLIB
|
||||||
const struct file_archive_file_backend *stream_backend =
|
const struct file_archive_file_backend *stream_backend =
|
||||||
file_archive_get_default_file_backend();
|
file_archive_get_zlib_file_backend();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (read_from != 1 || ret <= 0)
|
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))))
|
switch (msg_hash_to_file_type(msg_hash_calculate(path_get_extension(name))))
|
||||||
{
|
{
|
||||||
case FILE_TYPE_COMPRESSED:
|
case FILE_TYPE_COMPRESSED:
|
||||||
#ifdef HAVE_ZLIB
|
#ifdef HAVE_COMPRESSION
|
||||||
db->type = DATABASE_TYPE_ITERATE_ZIP;
|
db->type = DATABASE_TYPE_ITERATE_ARCHIVE;
|
||||||
memset(&db->state, 0, sizeof(file_archive_transfer_t));
|
memset(&db->state, 0, sizeof(file_archive_transfer_t));
|
||||||
db_state->zip_name[0] = '\0';
|
db_state->archive_name[0] = '\0';
|
||||||
db->state.type = ZLIB_TRANSFER_INIT;
|
db->state.type = ARCHIVE_TRANSFER_INIT;
|
||||||
return file_get_crc(db_state, name, &db_state->zip_crc);
|
return file_get_crc(db_state, name, &db_state->archive_crc);
|
||||||
#else
|
#else
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
@ -226,7 +226,7 @@ static int task_database_iterate_playlist(
|
|||||||
static int database_info_list_iterate_end_no_match(
|
static int database_info_list_iterate_end_no_match(
|
||||||
database_state_handle_t *db_state)
|
database_state_handle_t *db_state)
|
||||||
{
|
{
|
||||||
/* Reached end of database list,
|
/* Reached end of database list,
|
||||||
* CRC match probably didn't succeed. */
|
* CRC match probably didn't succeed. */
|
||||||
db_state->list_index = 0;
|
db_state->list_index = 0;
|
||||||
db_state->entry_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;
|
const char *new_database = db_state->list->elems[db_state->list_index].data;
|
||||||
#if 0
|
#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);
|
(unsigned)db_state->list->size, new_database);
|
||||||
#endif
|
#endif
|
||||||
if (db_state->info)
|
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(
|
static int database_info_list_iterate_found_match(
|
||||||
database_state_handle_t *db_state,
|
database_state_handle_t *db_state,
|
||||||
database_info_handle_t *db,
|
database_info_handle_t *db,
|
||||||
const char *zip_name
|
const char *archive_name
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
char db_crc[PATH_MAX_LENGTH] = {0};
|
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};
|
char entry_path_str[PATH_MAX_LENGTH] = {0};
|
||||||
playlist_t *playlist = NULL;
|
playlist_t *playlist = NULL;
|
||||||
settings_t *settings = config_get_ptr();
|
settings_t *settings = config_get_ptr();
|
||||||
const char *db_path =
|
const char *db_path =
|
||||||
db_state->list->elems[db_state->list_index].data;
|
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;
|
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];
|
&db_state->info->list[db_state->entry_index];
|
||||||
|
|
||||||
fill_short_pathname_representation_noext(db_playlist_base_str,
|
fill_short_pathname_representation_noext(db_playlist_base_str,
|
||||||
@ -296,8 +296,8 @@ static int database_info_list_iterate_found_match(
|
|||||||
if (entry_path)
|
if (entry_path)
|
||||||
strlcpy(entry_path_str, entry_path, sizeof(entry_path_str));
|
strlcpy(entry_path_str, entry_path, sizeof(entry_path_str));
|
||||||
|
|
||||||
if (!string_is_empty(zip_name))
|
if (!string_is_empty(archive_name))
|
||||||
fill_pathname_join_delim(entry_path_str, entry_path_str, zip_name,
|
fill_pathname_join_delim(entry_path_str, entry_path_str, archive_name,
|
||||||
'#', sizeof(entry_path_str));
|
'#', sizeof(entry_path_str));
|
||||||
|
|
||||||
#if 0
|
#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("Playlist Path: %s\n", db_playlist_path);
|
||||||
RARCH_LOG("Entry Path: %s\n", entry_path);
|
RARCH_LOG("Entry Path: %s\n", entry_path);
|
||||||
RARCH_LOG("Playlist not NULL: %d\n", playlist != NULL);
|
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);
|
RARCH_LOG("entry path str: %s\n", entry_path_str);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -332,7 +332,7 @@ static int database_info_list_iterate_found_match(
|
|||||||
return 0;
|
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. */
|
* match, go to the next database. */
|
||||||
static int database_info_list_iterate_next(
|
static int database_info_list_iterate_next(
|
||||||
database_state_handle_t *db_state
|
database_state_handle_t *db_state
|
||||||
@ -353,7 +353,7 @@ static int task_database_iterate_crc_lookup(
|
|||||||
const char *zip_entry)
|
const char *zip_entry)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (!db_state->list ||
|
if (!db_state->list ||
|
||||||
(unsigned)db_state->list_index == (unsigned)db_state->list->size)
|
(unsigned)db_state->list_index == (unsigned)db_state->list->size)
|
||||||
return database_info_list_iterate_end_no_match(db_state);
|
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};
|
char query[50] = {0};
|
||||||
snprintf(query, sizeof(query),
|
snprintf(query, sizeof(query),
|
||||||
"{crc:or(b\"%08X\",b\"%08X\")}",
|
"{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);
|
database_info_list_iterate_new(db_state, query);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (db_state->info)
|
if (db_state->info)
|
||||||
{
|
{
|
||||||
database_info_t *db_info_entry =
|
database_info_t *db_info_entry =
|
||||||
&db_state->info->list[db_state->entry_index];
|
&db_state->info->list[db_state->entry_index];
|
||||||
|
|
||||||
if (db_info_entry && db_info_entry->crc32)
|
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",
|
RARCH_LOG("CRC32: 0x%08X , entry CRC32: 0x%08X (%s).\n",
|
||||||
db_state->crc, db_info_entry->crc32, db_info_entry->name);
|
db_state->crc, db_info_entry->crc32, db_info_entry->name);
|
||||||
#endif
|
#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(
|
return database_info_list_iterate_found_match(
|
||||||
db_state, db, NULL);
|
db_state, db, NULL);
|
||||||
if (db_state->crc == db_info_entry->crc32)
|
if (db_state->crc == db_info_entry->crc32)
|
||||||
@ -404,7 +404,7 @@ static int task_database_iterate_crc_lookup(
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int task_database_iterate_playlist_zip(
|
static int task_database_iterate_playlist_archive(
|
||||||
database_state_handle_t *db_state,
|
database_state_handle_t *db_state,
|
||||||
database_info_handle_t *db, const char *name)
|
database_info_handle_t *db, const char *name)
|
||||||
{
|
{
|
||||||
@ -412,7 +412,7 @@ static int task_database_iterate_playlist_zip(
|
|||||||
#ifdef HAVE_ZLIB
|
#ifdef HAVE_ZLIB
|
||||||
if (db_state->crc != 0)
|
if (db_state->crc != 0)
|
||||||
return task_database_iterate_crc_lookup(
|
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,
|
if (file_archive_parse_file_iterate(&db->state,
|
||||||
&returnerr, name, NULL, zlib_compare_crc32,
|
&returnerr, name, NULL, zlib_compare_crc32,
|
||||||
@ -468,14 +468,14 @@ static int task_database_iterate_serial_lookup(
|
|||||||
database_state_handle_t *db_state,
|
database_state_handle_t *db_state,
|
||||||
database_info_handle_t *db, const char *name)
|
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)
|
(unsigned)db_state->list_index == (unsigned)db_state->list->size)
|
||||||
return database_info_list_iterate_end_no_match(db_state);
|
return database_info_list_iterate_end_no_match(db_state);
|
||||||
|
|
||||||
if (db_state->entry_index == 0)
|
if (db_state->entry_index == 0)
|
||||||
{
|
{
|
||||||
char query[50] = {0};
|
char query[50] = {0};
|
||||||
char *serial_buf =
|
char *serial_buf =
|
||||||
bin_to_hex_alloc((uint8_t*)db_state->serial, 10 * sizeof(uint8_t));
|
bin_to_hex_alloc((uint8_t*)db_state->serial, 10 * sizeof(uint8_t));
|
||||||
|
|
||||||
if (!serial_buf)
|
if (!serial_buf)
|
||||||
@ -536,8 +536,8 @@ static int task_database_iterate(database_state_handle_t *db_state,
|
|||||||
{
|
{
|
||||||
case DATABASE_TYPE_ITERATE:
|
case DATABASE_TYPE_ITERATE:
|
||||||
return task_database_iterate_playlist(db_state, db, name);
|
return task_database_iterate_playlist(db_state, db, name);
|
||||||
case DATABASE_TYPE_ITERATE_ZIP:
|
case DATABASE_TYPE_ITERATE_ARCHIVE:
|
||||||
return task_database_iterate_playlist_zip(db_state, db, name);
|
return task_database_iterate_playlist_archive(db_state, db, name);
|
||||||
case DATABASE_TYPE_ITERATE_LUTRO:
|
case DATABASE_TYPE_ITERATE_LUTRO:
|
||||||
return task_database_iterate_playlist_lutro(db_state, db, name);
|
return task_database_iterate_playlist_lutro(db_state, db, name);
|
||||||
case DATABASE_TYPE_SERIAL_LOOKUP:
|
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;
|
db_handle_t *db = (db_handle_t*)task->state;
|
||||||
database_info_handle_t *dbinfo = db->handle;
|
database_info_handle_t *dbinfo = db->handle;
|
||||||
database_state_handle_t *dbstate = &db->state;
|
database_state_handle_t *dbstate = &db->state;
|
||||||
const char *name = dbinfo ?
|
const char *name = dbinfo ?
|
||||||
dbinfo->list->elems[dbinfo->list_ptr].data : NULL;
|
dbinfo->list->elems[dbinfo->list_ptr].data : NULL;
|
||||||
|
|
||||||
if (!dbinfo || task->cancelled)
|
if (!dbinfo || task->cancelled)
|
||||||
|
@ -35,7 +35,7 @@ typedef struct
|
|||||||
|
|
||||||
char *callback_error;
|
char *callback_error;
|
||||||
|
|
||||||
file_archive_transfer_t zlib;
|
file_archive_transfer_t archive;
|
||||||
} decompress_state_t;
|
} decompress_state_t;
|
||||||
|
|
||||||
static int file_decompressed_target_file(const char *name,
|
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;
|
bool retdec = false;
|
||||||
decompress_state_t *dec = (decompress_state_t*)task->state;
|
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,
|
&retdec, dec->source_file,
|
||||||
dec->valid_ext, file_decompressed, dec);
|
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)
|
if (task->cancelled || ret != 0)
|
||||||
{
|
{
|
||||||
task->error = dec->callback_error;
|
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);
|
task_decompress_handler_finished(task, dec);
|
||||||
}
|
}
|
||||||
@ -178,16 +178,16 @@ static void task_decompress_handler_target_file(retro_task_t *task)
|
|||||||
{
|
{
|
||||||
bool retdec;
|
bool retdec;
|
||||||
decompress_state_t *dec = (decompress_state_t*)task->state;
|
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,
|
&retdec, dec->source_file,
|
||||||
dec->valid_ext, file_decompressed_target_file, dec);
|
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)
|
if (task->cancelled || ret != 0)
|
||||||
{
|
{
|
||||||
task->error = dec->callback_error;
|
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);
|
task_decompress_handler_finished(task, dec);
|
||||||
}
|
}
|
||||||
@ -197,16 +197,16 @@ static void task_decompress_handler_subdir(retro_task_t *task)
|
|||||||
{
|
{
|
||||||
bool retdec;
|
bool retdec;
|
||||||
decompress_state_t *dec = (decompress_state_t*)task->state;
|
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,
|
&retdec, dec->source_file,
|
||||||
dec->valid_ext, file_decompressed_subdir, dec);
|
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)
|
if (task->cancelled || ret != 0)
|
||||||
{
|
{
|
||||||
task->error = dec->callback_error;
|
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);
|
task_decompress_handler_finished(task, dec);
|
||||||
}
|
}
|
||||||
@ -286,7 +286,7 @@ bool task_push_decompress(
|
|||||||
s->target_dir = strdup(target_dir);
|
s->target_dir = strdup(target_dir);
|
||||||
|
|
||||||
s->valid_ext = valid_ext ? strdup(valid_ext) : NULL;
|
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));
|
t = (retro_task_t*)calloc(1, sizeof(*t));
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user