Merge branch 'archive_cleanup_1' of git://github.com/schellingb/RetroArch

This commit is contained in:
twinaphex 2020-07-13 21:30:02 +02:00
commit c84aa90075
6 changed files with 493 additions and 543 deletions

View File

@ -389,16 +389,6 @@ database_info_handle_t *database_info_dir_init(const char *dir,
db->list_ptr = 0; db->list_ptr = 0;
db->list = list; db->list = list;
db->state.type = ARCHIVE_TRANSFER_NONE;
db->state.archive_size = 0;
db->state.start_delta = 0;
db->state.handle = NULL;
db->state.stream = NULL;
db->state.footer = NULL;
db->state.directory = NULL;
db->state.data = NULL;
db->state.backend = NULL;
return db; return db;
} }
@ -430,16 +420,6 @@ database_info_handle_t *database_info_file_init(const char *path,
db->list_ptr = 0; db->list_ptr = 0;
db->list = list; db->list = list;
db->state.type = ARCHIVE_TRANSFER_NONE;
db->state.archive_size = 0;
db->state.start_delta = 0;
db->state.handle = NULL;
db->state.stream = NULL;
db->state.footer = NULL;
db->state.directory = NULL;
db->state.data = NULL;
db->state.backend = NULL;
return db; return db;
} }

View File

@ -76,7 +76,6 @@ 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;
file_archive_transfer_t state;
} database_info_handle_t; } database_info_handle_t;
typedef struct typedef struct

View File

@ -24,19 +24,6 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef HAVE_MMAP
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#endif
#include <compat/strl.h> #include <compat/strl.h>
#include <file/archive_file.h> #include <file/archive_file.h>
#include <file/file_path.h> #include <file/file_path.h>
@ -45,117 +32,12 @@
#include <lists/string_list.h> #include <lists/string_list.h>
#include <string/stdstring.h> #include <string/stdstring.h>
struct file_archive_file_data
{
#ifdef HAVE_MMAP #ifdef HAVE_MMAP
int fd; #include <fcntl.h>
#endif #include <errno.h>
void *data; #include <unistd.h>
size_t size; #include <sys/mman.h>
}; #include <sys/stat.h>
static size_t file_archive_size(file_archive_file_data_t *data)
{
if (!data)
return 0;
return data->size;
}
static const uint8_t *file_archive_data(file_archive_file_data_t *data)
{
if (!data)
return NULL;
return (const uint8_t*)data->data;
}
#ifdef HAVE_MMAP
/* Closes, unmaps and frees. */
static void file_archive_free(file_archive_file_data_t *data)
{
if (!data)
return;
if (data->data)
munmap(data->data, data->size);
if (data->fd >= 0)
close(data->fd);
free(data);
}
static file_archive_file_data_t* file_archive_open(const char *path)
{
file_archive_file_data_t *data = (file_archive_file_data_t*)
malloc(sizeof(*data));
if (!data)
return NULL;
data->fd = open(path, O_RDONLY);
data->data = NULL;
data->size = 0;
/* Failed to open archive. */
if (data->fd < 0)
goto error;
data->size = path_get_size(path);
if (!data->size)
return data;
data->data = mmap(NULL,
data->size, PROT_READ, MAP_SHARED, data->fd, 0);
if (data->data == MAP_FAILED)
{
data->data = NULL;
/* Failed to mmap() file */
goto error;
}
return data;
error:
file_archive_free(data);
return NULL;
}
#else
/* Closes, unmaps and frees. */
static void file_archive_free(file_archive_file_data_t *data)
{
if (!data)
return;
if(data->data)
free(data->data);
free(data);
}
static file_archive_file_data_t* file_archive_open(const char *path)
{
int64_t ret = -1;
bool read_from_file = false;
file_archive_file_data_t *data = (file_archive_file_data_t*)
malloc(sizeof(*data));
if (!data)
return NULL;
data->data = NULL;
data->size = 0;
read_from_file = filestream_read_file(
path, &data->data, &ret);
/* Failed to open archive? */
if (!read_from_file || ret < 0)
goto error;
data->size = ret;
return data;
error:
file_archive_free(data);
return NULL;
}
#endif #endif
static int file_archive_get_file_list_cb( static int file_archive_get_file_list_cb(
@ -223,11 +105,17 @@ static int file_archive_extract_cb(const char *name, const char *valid_exts,
if (ext && string_list_find_elem(userdata->ext, ext)) if (ext && string_list_find_elem(userdata->ext, ext))
{ {
char new_path[PATH_MAX_LENGTH]; char new_path[PATH_MAX_LENGTH];
char wanted_file[PATH_MAX_LENGTH]; const char *delim;
const char *delim = NULL;
new_path[0] = wanted_file[0] = '\0'; delim = path_get_archive_delim(userdata->archive_path);
if (delim)
{
if (!string_is_equal_noncase(userdata->current_file_path, delim + 1))
return 1; /* keep searching for the right file */
}
new_path[0] = '\0';
if (userdata->extraction_directory) if (userdata->extraction_directory)
fill_pathname_join(new_path, userdata->extraction_directory, fill_pathname_join(new_path, userdata->extraction_directory,
path_basename(name), sizeof(new_path)); path_basename(name), sizeof(new_path));
@ -235,25 +123,14 @@ static int file_archive_extract_cb(const char *name, const char *valid_exts,
fill_pathname_resolve_relative(new_path, userdata->archive_path, fill_pathname_resolve_relative(new_path, userdata->archive_path,
path_basename(name), sizeof(new_path)); path_basename(name), sizeof(new_path));
userdata->first_extracted_file_path = strdup(new_path);
delim = path_get_archive_delim(userdata->archive_path);
if (delim)
{
strlcpy(wanted_file, delim + 1, sizeof(wanted_file));
if (!string_is_equal_noncase(userdata->current_file_path,
wanted_file))
return 1; /* keep searching for the right file */
}
else
strlcpy(wanted_file, userdata->archive_path, sizeof(wanted_file));
if (file_archive_perform_mode(new_path, if (file_archive_perform_mode(new_path,
valid_exts, cdata, cmode, csize, size, valid_exts, cdata, cmode, csize, size,
0, userdata)) checksum, userdata))
{
userdata->found_file = true; userdata->found_file = true;
userdata->first_extracted_file_path = strdup(new_path);
}
return 0; return 0;
} }
@ -280,14 +157,37 @@ static int file_archive_parse_file_init(file_archive_transfer_t *state,
if (!state->backend) if (!state->backend)
return -1; return -1;
state->handle = file_archive_open(path); state->archive_file = filestream_open(path,
if (!state->handle) RETRO_VFS_FILE_ACCESS_READ,
RETRO_VFS_FILE_ACCESS_HINT_NONE);
/* Failed to open archive. */
if (!state->archive_file)
return -1; return -1;
state->archive_size = (int32_t)file_archive_size(state->handle); state->archive_size = filestream_get_size(state->archive_file);
state->data = file_archive_data(state->handle);
state->footer = 0; #ifdef HAVE_MMAP
state->directory = 0; if (state->archive_size <= (256*1024*1024))
{
state->archive_mmap_fd = open(path, O_RDONLY);
if (state->archive_mmap_fd)
{
state->archive_mmap_data = (uint8_t*)mmap(NULL, (size_t)state->archive_size,
PROT_READ, MAP_SHARED, state->archive_mmap_fd, 0);
if (state->archive_mmap_data == (uint8_t*)MAP_FAILED)
{
close(state->archive_mmap_fd);
state->archive_mmap_fd = 0;
state->archive_mmap_data = NULL;
}
}
}
#endif
state->step_current = 0;
state->step_total = 0;
return state->backend->archive_parse_file_init(state, path); return state->backend->archive_parse_file_init(state, path);
} }
@ -295,35 +195,25 @@ static int file_archive_parse_file_init(file_archive_transfer_t *state,
/** /**
* 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.
* If NULL, allow all.
* @cdata : input data.
* @csize : size of input data.
* @size : output file size * @size : output file size
* @checksum : CRC32 checksum from input data. * @checksum : CRC32 checksum from input data.
* *
* Decompress data to file. * Write data to file.
* *
* Returns: true (1) on success, otherwise false (0). * Returns: true (1) on success, otherwise false (0).
**/ **/
static int file_archive_decompress_data_to_file( static int file_archive_decompress_data_to_file(
file_archive_transfer_t *transfer,
file_archive_file_handle_t *handle, file_archive_file_handle_t *handle,
int ret,
const char *path, const char *path,
const char *valid_exts,
const uint8_t *cdata,
uint32_t csize,
uint32_t size, uint32_t size,
uint32_t checksum) uint32_t checksum)
{ {
if (!handle || ret == -1) if (!handle)
{ return 0;
ret = 0;
goto end;
}
#if 0 #if 0
handle->real_checksum = handle->backend->stream_crc_calculate( handle->real_checksum = transfer->backend->stream_crc_calculate(
0, handle->data, size); 0, handle->data, size);
if (handle->real_checksum != checksum) if (handle->real_checksum != checksum)
{ {
@ -334,38 +224,14 @@ static int file_archive_decompress_data_to_file(
#endif #endif
if (!filestream_write_file(path, handle->data, size)) if (!filestream_write_file(path, handle->data, size))
{ return 0;
ret = false;
goto end;
}
end: return 1;
if (handle)
{
if (handle->backend)
{
if (handle->backend->stream_free)
{
#ifdef HAVE_7ZIP
if (handle->backend != &sevenzip_backend)
#endif
{
handle->backend->stream_free(handle->stream);
if (handle->data)
free(handle->data);
}
}
}
}
return ret;
} }
void file_archive_parse_file_iterate_stop(file_archive_transfer_t *state) void file_archive_parse_file_iterate_stop(file_archive_transfer_t *state)
{ {
if (!state || !state->handle) if (!state || !state->archive_file)
return; return;
state->type = ARCHIVE_TRANSFER_DEINIT; state->type = ARCHIVE_TRANSFER_DEINIT;
@ -392,7 +258,7 @@ int file_archive_parse_file_iterate(
{ {
if (userdata) if (userdata)
{ {
userdata->context = state->stream; userdata->transfer = state;
strlcpy(userdata->archive_path, file, strlcpy(userdata->archive_path, file,
sizeof(userdata->archive_path)); sizeof(userdata->archive_path));
} }
@ -402,14 +268,13 @@ int file_archive_parse_file_iterate(
state->type = ARCHIVE_TRANSFER_DEINIT_ERROR; state->type = ARCHIVE_TRANSFER_DEINIT_ERROR;
break; break;
case ARCHIVE_TRANSFER_ITERATE: case ARCHIVE_TRANSFER_ITERATE:
if (file_archive_get_file_backend(file)) if (state->backend)
{ {
const struct file_archive_file_backend *backend = int ret = state->backend->archive_parse_file_iterate_step(
file_archive_get_file_backend(file); state->context, valid_exts, userdata, file_cb);
int ret =
backend->archive_parse_file_iterate_step(state,
valid_exts, userdata, file_cb);
if (ret == 1)
state->step_current++; /* found another file */
if (ret != 1) if (ret != 1)
state->type = ARCHIVE_TRANSFER_DEINIT; state->type = ARCHIVE_TRANSFER_DEINIT;
if (ret == -1) if (ret == -1)
@ -422,25 +287,31 @@ int file_archive_parse_file_iterate(
case ARCHIVE_TRANSFER_DEINIT_ERROR: case ARCHIVE_TRANSFER_DEINIT_ERROR:
*returnerr = false; *returnerr = false;
case ARCHIVE_TRANSFER_DEINIT: case ARCHIVE_TRANSFER_DEINIT:
if (state->handle) if (state->context)
{ {
file_archive_free(state->handle); if (state->backend->archive_parse_file_free)
state->handle = NULL; state->backend->archive_parse_file_free(state->context);
state->context = NULL;
} }
if (state->stream && state->backend) if (state->archive_file)
{ {
if (state->backend->stream_free) filestream_close(state->archive_file);
state->backend->stream_free(state->stream); state->archive_file = NULL;
}
if (state->stream) #ifdef HAVE_MMAP
free(state->stream); if (state->archive_mmap_data)
{
state->stream = NULL; munmap(state->archive_mmap_data, (size_t)state->archive_size);
close(state->archive_mmap_fd);
state->archive_mmap_fd = 0;
state->archive_mmap_data = NULL;
}
#endif
if (userdata) if (userdata)
userdata->context = NULL; userdata->transfer = NULL;
}
break; break;
} }
@ -471,12 +342,15 @@ static bool file_archive_walk(const char *file, const char *valid_exts,
bool returnerr = true; bool returnerr = true;
state.type = ARCHIVE_TRANSFER_INIT; state.type = ARCHIVE_TRANSFER_INIT;
state.archive_file = NULL;
#ifdef HAVE_MMAP
state.archive_mmap_fd = 0;
state.archive_mmap_data = NULL;
#endif
state.archive_size = 0; state.archive_size = 0;
state.handle = NULL; state.context = NULL;
state.stream = NULL; state.step_total = 0;
state.footer = NULL; state.step_current = 0;
state.directory = NULL;
state.data = NULL;
state.backend = NULL; state.backend = NULL;
for (;;) for (;;)
@ -491,17 +365,10 @@ static bool file_archive_walk(const char *file, const char *valid_exts,
int file_archive_parse_file_progress(file_archive_transfer_t *state) int file_archive_parse_file_progress(file_archive_transfer_t *state)
{ {
ptrdiff_t delta = 0; if (!state || state->step_total == 0)
if (!state || state->archive_size == 0)
return 0; return 0;
delta = state->directory - state->data; return (int)((state->step_current * 100) / (state->step_total));
if (!state->start_delta)
state->start_delta = delta;
return (int)(((delta - state->start_delta) * 100) / (state->archive_size - state->start_delta));
} }
/** /**
@ -537,15 +404,10 @@ bool file_archive_extract_file(
userdata.list = NULL; userdata.list = NULL;
userdata.found_file = false; userdata.found_file = false;
userdata.list_only = false; userdata.list_only = false;
userdata.context = NULL;
userdata.crc = 0; userdata.crc = 0;
userdata.transfer = NULL;
userdata.dec = NULL; userdata.dec = NULL;
userdata.decomp_state.opt_file = NULL;
userdata.decomp_state.needle = NULL;
userdata.decomp_state.size = 0;
userdata.decomp_state.found = false;
if (!list) if (!list)
{ {
ret = false; ret = false;
@ -599,15 +461,10 @@ struct string_list *file_archive_get_file_list(const char *path,
userdata.list = string_list_new(); userdata.list = string_list_new();
userdata.found_file = false; userdata.found_file = false;
userdata.list_only = true; userdata.list_only = true;
userdata.context = NULL;
userdata.crc = 0; userdata.crc = 0;
userdata.transfer = NULL;
userdata.dec = NULL; userdata.dec = NULL;
userdata.decomp_state.opt_file = NULL;
userdata.decomp_state.needle = NULL;
userdata.decomp_state.size = 0;
userdata.decomp_state.found = false;
if (!userdata.list) if (!userdata.list)
goto error; goto error;
@ -627,45 +484,29 @@ bool file_archive_perform_mode(const char *path, 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, struct archive_extract_userdata *userdata) uint32_t crc32, struct archive_extract_userdata *userdata)
{ {
switch (cmode)
{
case ARCHIVE_MODE_UNCOMPRESSED:
if (!filestream_write_file(path, cdata, size))
return false;
break;
case ARCHIVE_MODE_COMPRESSED:
{
int ret = 0;
file_archive_file_handle_t handle; file_archive_file_handle_t handle;
int ret;
if (!userdata->transfer || !userdata->transfer->backend)
return false;
handle.stream = userdata->context;
handle.data = NULL; handle.data = NULL;
handle.real_checksum = 0; handle.real_checksum = 0;
handle.backend = file_archive_get_file_backend(userdata->archive_path);
if (!handle.backend) if (!userdata->transfer->backend->stream_decompress_data_to_file_init(
return false; userdata->transfer->context, &handle, cdata, cmode, csize, size))
if (!handle.backend->stream_decompress_data_to_file_init(&handle,
cdata, csize, size))
return false; return false;
do do
{ {
ret = handle.backend->stream_decompress_data_to_file_iterate( ret = userdata->transfer->backend->stream_decompress_data_to_file_iterate(
handle.stream); userdata->transfer->context, &handle);
}while (ret == 0); }while (ret == 0);
if (!file_archive_decompress_data_to_file(&handle, if (ret == -1 || !file_archive_decompress_data_to_file(
ret, path, valid_exts, userdata->transfer, &handle, path,
cdata, csize, size, crc32)) size, crc32))
return false; return false;
}
break;
default:
return false;
}
return true; return true;
} }
@ -841,12 +682,15 @@ uint32_t file_archive_get_file_crc32(const char *path)
} }
state.type = ARCHIVE_TRANSFER_INIT; state.type = ARCHIVE_TRANSFER_INIT;
state.archive_file = NULL;
#ifdef HAVE_MMAP
state.archive_mmap_fd = 0;
state.archive_mmap_data = NULL;
#endif
state.archive_size = 0; state.archive_size = 0;
state.handle = NULL; state.context = NULL;
state.stream = NULL; state.step_total = 0;
state.footer = NULL; state.step_current = 0;
state.directory = NULL;
state.data = NULL;
state.backend = NULL; state.backend = NULL;
/* Initialize and open archive first. /* Initialize and open archive first.
@ -881,8 +725,5 @@ uint32_t file_archive_get_file_crc32(const char *path)
file_archive_parse_file_iterate_stop(&state); file_archive_parse_file_iterate_stop(&state);
if (userdata.crc)
return userdata.crc; return userdata.crc;
return 0;
} }

View File

@ -55,10 +55,10 @@ struct sevenzip_context_t
CSzArEx db; CSzArEx db;
size_t temp_size; size_t temp_size;
uint32_t block_index; uint32_t block_index;
uint32_t index; uint32_t parse_index;
uint32_t decompress_index;
uint32_t packIndex; uint32_t packIndex;
uint8_t *output; uint8_t *output;
file_archive_file_handle_t *handle;
}; };
static void *sevenzip_stream_alloc_impl(void *p, size_t size) static void *sevenzip_stream_alloc_impl(void *p, size_t size)
@ -97,14 +97,13 @@ static void* sevenzip_stream_new(void)
sevenzip_context->allocTempImp.Free = sevenzip_stream_free_impl; sevenzip_context->allocTempImp.Free = sevenzip_stream_free_impl;
sevenzip_context->block_index = 0xFFFFFFFF; sevenzip_context->block_index = 0xFFFFFFFF;
sevenzip_context->output = NULL; sevenzip_context->output = NULL;
sevenzip_context->handle = NULL;
return sevenzip_context; return sevenzip_context;
} }
static void sevenzip_stream_free(void *data) static void sevenzip_parse_file_free(void *context)
{ {
struct sevenzip_context_t *sevenzip_context = (struct sevenzip_context_t*)data; struct sevenzip_context_t *sevenzip_context = (struct sevenzip_context_t*)context;
if (!sevenzip_context) if (!sevenzip_context)
return; return;
@ -113,11 +112,12 @@ static void sevenzip_stream_free(void *data)
{ {
IAlloc_Free(&sevenzip_context->allocImp, sevenzip_context->output); IAlloc_Free(&sevenzip_context->allocImp, sevenzip_context->output);
sevenzip_context->output = NULL; sevenzip_context->output = NULL;
sevenzip_context->handle->data = NULL;
} }
SzArEx_Free(&sevenzip_context->db, &sevenzip_context->allocImp); SzArEx_Free(&sevenzip_context->db, &sevenzip_context->allocImp);
File_Close(&sevenzip_context->archiveStream.file); File_Close(&sevenzip_context->archiveStream.file);
free(sevenzip_context);
} }
/* Extract the relative path (needle) from a 7z archive /* Extract the relative path (needle) from a 7z archive
@ -125,7 +125,7 @@ static void sevenzip_stream_free(void *data)
* If optional_outfile is set, extract to that instead * If optional_outfile is set, extract to that instead
* and don't allocate buffer. * and don't allocate buffer.
*/ */
static int sevenzip_file_read( static int64_t sevenzip_file_read(
const char *path, const char *path,
const char *needle, void **buf, const char *needle, void **buf,
const char *optional_outfile) const char *optional_outfile)
@ -136,7 +136,7 @@ static int sevenzip_file_read(
ISzAlloc allocTempImp; ISzAlloc allocTempImp;
CSzArEx db; CSzArEx db;
uint8_t *output = 0; uint8_t *output = 0;
long outsize = -1; int64_t outsize = -1;
/*These are the allocation routines. /*These are the allocation routines.
* Currently using the non-standard 7zip choices. */ * Currently using the non-standard 7zip choices. */
@ -255,7 +255,7 @@ static int sevenzip_file_read(
if (res != SZ_OK) if (res != SZ_OK)
break; /* This goes to the error section. */ break; /* This goes to the error section. */
outsize = outSizeProcessed; outsize = (int64_t)outSizeProcessed;
if (optional_outfile) if (optional_outfile)
{ {
@ -275,7 +275,7 @@ static int sevenzip_file_read(
* We would however need to realloc anyways, because RetroArch * We would however need to realloc anyways, because RetroArch
* expects a \0 at the end, therefore we allocate new, * expects a \0 at the end, therefore we allocate new,
* copy and free the old one. */ * copy and free the old one. */
*buf = malloc(outsize + 1); *buf = malloc((size_t)(outsize + 1));
((char*)(*buf))[outsize] = '\0'; ((char*)(*buf))[outsize] = '\0';
memcpy(*buf,output + offset,outsize); memcpy(*buf,output + offset,outsize);
} }
@ -301,28 +301,29 @@ static int sevenzip_file_read(
SzArEx_Free(&db, &allocImp); SzArEx_Free(&db, &allocImp);
File_Close(&archiveStream.file); File_Close(&archiveStream.file);
return (int)outsize; return outsize;
} }
static bool sevenzip_stream_decompress_data_to_file_init( static bool sevenzip_stream_decompress_data_to_file_init(
file_archive_file_handle_t *handle, void *context, file_archive_file_handle_t *handle,
const uint8_t *cdata, uint32_t csize, uint32_t size) const uint8_t *cdata, unsigned cmode, uint32_t csize, uint32_t size)
{ {
struct sevenzip_context_t *sevenzip_context = struct sevenzip_context_t *sevenzip_context =
(struct sevenzip_context_t*)handle->stream; (struct sevenzip_context_t*)context;
if (!sevenzip_context) if (!sevenzip_context)
return false; return false;
sevenzip_context->handle = handle; sevenzip_context->decompress_index = (uint32_t)(size_t)cdata;
return true; return true;
} }
static int sevenzip_stream_decompress_data_to_file_iterate(void *data) static int sevenzip_stream_decompress_data_to_file_iterate(
void *context, file_archive_file_handle_t *handle)
{ {
struct sevenzip_context_t *sevenzip_context = struct sevenzip_context_t *sevenzip_context =
(struct sevenzip_context_t*)data; (struct sevenzip_context_t*)context;
SRes res = SZ_ERROR_FAIL; SRes res = SZ_ERROR_FAIL;
size_t output_size = 0; size_t output_size = 0;
@ -330,7 +331,7 @@ static int sevenzip_stream_decompress_data_to_file_iterate(void *data)
size_t outSizeProcessed = 0; size_t outSizeProcessed = 0;
res = SzArEx_Extract(&sevenzip_context->db, res = SzArEx_Extract(&sevenzip_context->db,
&sevenzip_context->lookStream.s, sevenzip_context->index, &sevenzip_context->lookStream.s, sevenzip_context->decompress_index,
&sevenzip_context->block_index, &sevenzip_context->output, &sevenzip_context->block_index, &sevenzip_context->output,
&output_size, &offset, &outSizeProcessed, &output_size, &offset, &outSizeProcessed,
&sevenzip_context->allocImp, &sevenzip_context->allocTempImp); &sevenzip_context->allocImp, &sevenzip_context->allocTempImp);
@ -338,8 +339,8 @@ static int sevenzip_stream_decompress_data_to_file_iterate(void *data)
if (res != SZ_OK) if (res != SZ_OK)
return 0; return 0;
if (sevenzip_context->handle) if (handle)
sevenzip_context->handle->data = sevenzip_context->output + offset; handle->data = sevenzip_context->output + offset;
return 1; return 1;
} }
@ -347,16 +348,21 @@ static int sevenzip_stream_decompress_data_to_file_iterate(void *data)
static int sevenzip_parse_file_init(file_archive_transfer_t *state, static int sevenzip_parse_file_init(file_archive_transfer_t *state,
const char *file) const char *file)
{ {
struct sevenzip_context_t *sevenzip_context = uint8_t magic_buf[SEVENZIP_MAGIC_LEN];
(struct sevenzip_context_t*)sevenzip_stream_new(); struct sevenzip_context_t *sevenzip_context = NULL;
if (state->archive_size < SEVENZIP_MAGIC_LEN) if (state->archive_size < SEVENZIP_MAGIC_LEN)
goto error; goto error;
if (string_is_not_equal_fast(state->data, SEVENZIP_MAGIC, SEVENZIP_MAGIC_LEN)) filestream_seek(state->archive_file, 0, SEEK_SET);
if (filestream_read(state->archive_file, magic_buf, SEVENZIP_MAGIC_LEN) != SEVENZIP_MAGIC_LEN)
goto error; goto error;
state->stream = sevenzip_context; if (string_is_not_equal_fast(magic_buf, SEVENZIP_MAGIC, SEVENZIP_MAGIC_LEN))
goto error;
sevenzip_context = (struct sevenzip_context_t*)sevenzip_stream_new();
state->context = sevenzip_context;
#if defined(_WIN32) && defined(USE_WINDOWS_FILE) && !defined(LEGACY_WIN32) #if defined(_WIN32) && defined(USE_WINDOWS_FILE) && !defined(LEGACY_WIN32)
if (!string_is_empty(file)) if (!string_is_empty(file))
@ -392,27 +398,28 @@ static int sevenzip_parse_file_init(file_archive_transfer_t *state,
&sevenzip_context->allocImp, &sevenzip_context->allocTempImp) != SZ_OK) &sevenzip_context->allocImp, &sevenzip_context->allocTempImp) != SZ_OK)
goto error; goto error;
state->step_total = sevenzip_context->db.db.NumFiles;
return 0; return 0;
error: error:
if (sevenzip_context) if (sevenzip_context)
sevenzip_stream_free(sevenzip_context); sevenzip_parse_file_free(sevenzip_context);
return -1; return -1;
} }
static int sevenzip_parse_file_iterate_step_internal( static int sevenzip_parse_file_iterate_step_internal(
file_archive_transfer_t *state, char *filename, struct sevenzip_context_t *sevenzip_context, char *filename,
const uint8_t **cdata, unsigned *cmode, const uint8_t **cdata, unsigned *cmode,
uint32_t *size, uint32_t *csize, uint32_t *checksum, uint32_t *size, uint32_t *csize, uint32_t *checksum,
unsigned *payback, struct archive_extract_userdata *userdata) unsigned *payback, struct archive_extract_userdata *userdata)
{ {
struct sevenzip_context_t *sevenzip_context = (struct sevenzip_context_t*)state->stream; const CSzFileItem *file = sevenzip_context->db.db.Files + sevenzip_context->parse_index;
const CSzFileItem *file = sevenzip_context->db.db.Files + sevenzip_context->index;
if (sevenzip_context->index < sevenzip_context->db.db.NumFiles) if (sevenzip_context->parse_index < sevenzip_context->db.db.NumFiles)
{ {
size_t len = SzArEx_GetFileNameUtf16(&sevenzip_context->db, size_t len = SzArEx_GetFileNameUtf16(&sevenzip_context->db,
sevenzip_context->index, NULL); sevenzip_context->parse_index, NULL);
uint64_t compressed_size = 0; uint64_t compressed_size = 0;
if (sevenzip_context->packIndex < sevenzip_context->db.db.NumPackStreams) if (sevenzip_context->packIndex < sevenzip_context->db.db.NumPackStreams)
@ -432,7 +439,7 @@ static int sevenzip_parse_file_iterate_step_internal(
infile[0] = '\0'; infile[0] = '\0';
SzArEx_GetFileNameUtf16(&sevenzip_context->db, sevenzip_context->index, SzArEx_GetFileNameUtf16(&sevenzip_context->db, sevenzip_context->parse_index,
temp); temp);
if (temp) if (temp)
@ -447,10 +454,12 @@ static int sevenzip_parse_file_iterate_step_internal(
strlcpy(filename, infile, PATH_MAX_LENGTH); strlcpy(filename, infile, PATH_MAX_LENGTH);
*cmode = ARCHIVE_MODE_COMPRESSED; *cmode = 0; /* unused for 7zip */
*checksum = file->Crc; *checksum = file->Crc;
*size = (uint32_t)file->Size; *size = (uint32_t)file->Size;
*csize = (uint32_t)compressed_size; *csize = (uint32_t)compressed_size;
*cdata = (uint8_t *)(size_t)sevenzip_context->parse_index;
} }
} }
else else
@ -461,7 +470,7 @@ static int sevenzip_parse_file_iterate_step_internal(
return 1; return 1;
} }
static int sevenzip_parse_file_iterate_step(file_archive_transfer_t *state, static int sevenzip_parse_file_iterate_step(void *context,
const char *valid_exts, const char *valid_exts,
struct archive_extract_userdata *userdata, file_archive_file_cb file_cb) struct archive_extract_userdata *userdata, file_archive_file_cb file_cb)
{ {
@ -471,12 +480,12 @@ static int sevenzip_parse_file_iterate_step(file_archive_transfer_t *state,
uint32_t csize = 0; uint32_t csize = 0;
unsigned cmode = 0; unsigned cmode = 0;
unsigned payload = 0; unsigned payload = 0;
struct sevenzip_context_t *sevenzip_context = NULL; struct sevenzip_context_t *sevenzip_context = (struct sevenzip_context_t*)context;
int ret; int ret;
userdata->current_file_path[0] = '\0'; userdata->current_file_path[0] = '\0';
ret = sevenzip_parse_file_iterate_step_internal(state, ret = sevenzip_parse_file_iterate_step_internal(sevenzip_context,
userdata->current_file_path, userdata->current_file_path,
&cdata, &cmode, &size, &csize, &cdata, &cmode, &size, &csize,
&checksum, &payload, userdata); &checksum, &payload, userdata);
@ -491,9 +500,7 @@ static int sevenzip_parse_file_iterate_step(file_archive_transfer_t *state,
csize, size, checksum, userdata)) csize, size, checksum, userdata))
return 0; return 0;
sevenzip_context = (struct sevenzip_context_t*)state->stream; sevenzip_context->parse_index += payload;
sevenzip_context->index += payload;
return 1; return 1;
} }
@ -505,13 +512,12 @@ static uint32_t sevenzip_stream_crc32_calculate(uint32_t crc,
} }
const struct file_archive_file_backend sevenzip_backend = { const struct file_archive_file_backend sevenzip_backend = {
sevenzip_stream_new, sevenzip_parse_file_init,
sevenzip_stream_free, sevenzip_parse_file_iterate_step,
sevenzip_parse_file_free,
sevenzip_stream_decompress_data_to_file_init, sevenzip_stream_decompress_data_to_file_init,
sevenzip_stream_decompress_data_to_file_iterate, sevenzip_stream_decompress_data_to_file_iterate,
sevenzip_stream_crc32_calculate, sevenzip_stream_crc32_calculate,
sevenzip_file_read, sevenzip_file_read,
sevenzip_parse_file_init,
sevenzip_parse_file_iterate_step,
"7z" "7z"
}; };

View File

@ -41,6 +41,23 @@
#define END_OF_CENTRAL_DIR_SIGNATURE 0x06054b50 #define END_OF_CENTRAL_DIR_SIGNATURE 0x06054b50
#endif #endif
enum file_archive_compression_mode
{
ZIP_MODE_STORED = 0,
ZIP_MODE_DEFLATED = 8
};
typedef struct
{
struct file_archive_transfer *state;
uint8_t *directory;
uint8_t *directory_entry;
uint8_t *directory_end;
void *current_stream;
uint8_t *compressed_data;
uint8_t *decompressed_data;
} zip_context_t;
static INLINE uint32_t read_le(const uint8_t *data, unsigned size) static INLINE uint32_t read_le(const uint8_t *data, unsigned size)
{ {
unsigned i; unsigned i;
@ -53,69 +70,147 @@ static INLINE uint32_t read_le(const uint8_t *data, unsigned size)
return val; return val;
} }
static void *zlib_stream_new(void) static void zip_context_free_stream(
zip_context_t *zip_context, bool keep_decompressed)
{ {
return zlib_inflate_backend.stream_new(); if (zip_context->current_stream)
} {
zlib_inflate_backend.stream_free(zip_context->current_stream);
static void zlib_stream_free(void *stream) zip_context->current_stream = NULL;
{ }
zlib_inflate_backend.stream_free(stream); if (zip_context->compressed_data)
{
#ifdef HAVE_MMAP
if (!zip_context->state->archive_mmap_data)
#endif
{
free(zip_context->compressed_data);
zip_context->compressed_data = NULL;
}
}
if (zip_context->decompressed_data && !keep_decompressed)
{
free(zip_context->decompressed_data);
zip_context->decompressed_data = NULL;
}
} }
static bool zlib_stream_decompress_data_to_file_init( static bool zlib_stream_decompress_data_to_file_init(
file_archive_file_handle_t *handle, void *context, file_archive_file_handle_t *handle,
const uint8_t *cdata, uint32_t csize, uint32_t size) const uint8_t *cdata, unsigned cmode, uint32_t csize, uint32_t size)
{ {
if (!handle) zip_context_t *zip_context = (zip_context_t *)context;
return false; struct file_archive_transfer *state = zip_context->state;
uint8_t local_header_buf[4];
uint8_t *local_header;
uint32_t offsetNL, offsetEL;
int64_t offsetData;
handle->stream = zlib_inflate_backend.stream_new(); /* free previous data and stream if left unfinished */
zip_context_free_stream(zip_context, false);
if (!handle->stream) /* seek past most of the local directory header */
#ifdef HAVE_MMAP
if (state->archive_mmap_data)
{
local_header = state->archive_mmap_data + (size_t)cdata + 26;
}
else
#endif
{
filestream_seek(state->archive_file, (int64_t)(size_t)cdata + 26, RETRO_VFS_SEEK_POSITION_START);
if (filestream_read(state->archive_file, local_header_buf, 4) != 4)
goto error;
local_header = local_header_buf;
}
offsetNL = read_le(local_header, 2); /* file name length */
offsetEL = read_le(local_header + 2, 2); /* extra field length */
offsetData = (int64_t)(size_t)cdata + 26 + 4 + offsetNL + offsetEL;
#ifdef HAVE_MMAP
if (state->archive_mmap_data)
{
zip_context->compressed_data = state->archive_mmap_data + (size_t)offsetData;
}
else
#endif
{
/* allocate memory for the compressed data */
zip_context->compressed_data = (uint8_t*)malloc(csize);
if (!zip_context->compressed_data)
goto error;
/* skip over name and extra data */
filestream_seek(state->archive_file, offsetData, RETRO_VFS_SEEK_POSITION_START);
if (filestream_read(state->archive_file, zip_context->compressed_data, csize) != csize)
goto error;
}
switch (cmode)
{
case ZIP_MODE_STORED:
handle->data = zip_context->compressed_data;
return true;
case ZIP_MODE_DEFLATED:
zip_context->current_stream = zlib_inflate_backend.stream_new();
if (!zip_context->current_stream)
goto error; goto error;
if (zlib_inflate_backend.define) if (zlib_inflate_backend.define)
zlib_inflate_backend.define(handle->stream, "window_bits", (uint32_t)-MAX_WBITS); zlib_inflate_backend.define(zip_context->current_stream, "window_bits", (uint32_t)-MAX_WBITS);
handle->data = (uint8_t*)malloc(size); zip_context->decompressed_data = (uint8_t*)malloc(size);
if (!handle->data) if (!zip_context->decompressed_data)
goto error; goto error;
zlib_inflate_backend.set_in(handle->stream, zlib_inflate_backend.set_in(zip_context->current_stream,
(const uint8_t*)cdata, csize); zip_context->compressed_data, csize);
zlib_inflate_backend.set_out(handle->stream, zlib_inflate_backend.set_out(zip_context->current_stream,
handle->data, size); zip_context->decompressed_data, size);
return true; return true;
}
error: error:
if (handle->stream) zip_context_free_stream(zip_context, false);
zlib_inflate_backend.stream_free(handle->stream);
if (handle->data)
free(handle->data);
return false; return false;
} }
static int zlib_stream_decompress_data_to_file_iterate(void *stream) static int zlib_stream_decompress_data_to_file_iterate(
void *context, file_archive_file_handle_t *handle)
{ {
zip_context_t *zip_context = (zip_context_t *)context;
bool zstatus; bool zstatus;
uint32_t rd, wn; uint32_t rd, wn;
enum trans_stream_error terror; enum trans_stream_error terror;
if (!stream) if (!zip_context->current_stream)
return -1; {
/* file was uncompressed or decompression finished before */
return 1;
}
zstatus = zlib_inflate_backend.trans(stream, false, &rd, &wn, &terror); zstatus = zlib_inflate_backend.trans(zip_context->current_stream, false, &rd, &wn, &terror);
if (!zstatus && terror != TRANS_STREAM_ERROR_BUFFER_FULL)
return -1;
if (zstatus && !terror) if (zstatus && !terror)
{
/* successfully decompressed entire file */
zip_context_free_stream(zip_context, true);
handle->data = zip_context->decompressed_data;
return 1; return 1;
}
if (!zstatus && terror != TRANS_STREAM_ERROR_BUFFER_FULL)
{
/* error during stream processing */
zip_context_free_stream(zip_context, false);
return -1;
}
/* still more data to process */
return 0; return 0;
} }
@ -126,49 +221,52 @@ static uint32_t zlib_stream_crc32_calculate(uint32_t crc,
} }
static bool zip_file_decompressed_handle( static bool zip_file_decompressed_handle(
file_archive_file_handle_t *handle, file_archive_transfer_t *transfer,
const uint8_t *cdata, uint32_t csize, file_archive_file_handle_t* handle,
const uint8_t *cdata, unsigned cmode, uint32_t csize,
uint32_t size, uint32_t crc32) uint32_t size, uint32_t crc32)
{ {
zip_context_t *zip_context = (zip_context_t *)transfer->context;
int ret = 0; int ret = 0;
handle->backend = &zlib_backend; transfer->backend = &zlib_backend;
if (!handle->backend->stream_decompress_data_to_file_init( if (!transfer->backend->stream_decompress_data_to_file_init(
handle, cdata, csize, size)) transfer->context, handle, cdata, cmode, csize, size))
return false; return false;
do do
{ {
ret = handle->backend->stream_decompress_data_to_file_iterate( ret = transfer->backend->stream_decompress_data_to_file_iterate(
handle->stream); transfer->context, handle);
}while (ret == 0); }while (ret == 0);
#if 0 #if 0
handle->real_checksum = handle->backend->stream_crc_calculate(0, handle->real_checksum = transfer->backend->stream_crc_calculate(0,
handle->data, size); handle->data, size);
if (handle->real_checksum != crc32) if (handle->real_checksum != crc32)
goto error; {
#endif
if (handle->stream)
free(handle->stream);
return true;
#if 0
error:
if (handle->stream)
free(handle->stream);
if (handle->data) if (handle->data)
free(handle->data); free(handle->data);
handle->stream = NULL;
handle->data = NULL; handle->data = NULL;
return false; return false;
}
#endif #endif
return true;
} }
typedef struct
{
char *opt_file;
char *needle;
void **buf;
size_t size;
bool found;
} decomp_state_t;
/* Extract the relative path (needle) from a /* Extract the relative path (needle) from a
* ZIP archive (path) and allocate a buffer for it to write it in. * ZIP archive (path) and allocate a buffer for it to write it in.
* *
@ -182,212 +280,251 @@ static int zip_file_decompressed(
uint32_t csize, uint32_t size, uint32_t csize, uint32_t size,
uint32_t crc32, struct archive_extract_userdata *userdata) uint32_t crc32, struct archive_extract_userdata *userdata)
{ {
decomp_state_t* decomp_state = (decomp_state_t*)userdata->cb_data;
char last_char = name[strlen(name) - 1]; char last_char = name[strlen(name) - 1];
/* Ignore directories. */ /* Ignore directories. */
if (last_char == '/' || last_char == '\\') if (last_char == '/' || last_char == '\\')
return 1; return 1;
if (strstr(name, userdata->decomp_state.needle)) if (strstr(name, decomp_state->needle))
{ {
bool goto_error = false;
file_archive_file_handle_t handle = {0}; file_archive_file_handle_t handle = {0};
userdata->decomp_state.found = true; if (zip_file_decompressed_handle(userdata->transfer,
&handle, cdata, cmode, csize, size, crc32))
if (zip_file_decompressed_handle(&handle,
cdata, csize, size, crc32))
{ {
if (userdata->decomp_state.opt_file != 0) if (decomp_state->opt_file != 0)
{ {
/* Called in case core has need_fullpath enabled. */ /* Called in case core has need_fullpath enabled. */
char *buf = (char*)malloc(size); bool success = filestream_write_file(decomp_state->opt_file, handle.data, size);
if (buf) free(handle.data);
{ handle.data = NULL;
memcpy(buf, handle.data, size);
if (!filestream_write_file(userdata->decomp_state.opt_file, buf, size)) decomp_state->size = 0;
goto_error = true;
}
free(buf); if (!success)
return -1;
userdata->decomp_state.size = 0;
} }
else else
{ {
/* Called in case core has need_fullpath disabled. /* Called in case core has need_fullpath disabled.
* Will copy decompressed content directly into * Will move decompressed content directly into
* RetroArch's ROM buffer. */ * RetroArch's ROM buffer. */
*userdata->decomp_state.buf = malloc(size); *decomp_state->buf = handle.data;
memcpy(*userdata->decomp_state.buf, handle.data, size); handle.data = NULL;
userdata->decomp_state.size = size; decomp_state->size = size;
} }
} }
if (handle.data) decomp_state->found = true;
free(handle.data);
if (goto_error)
return 0;
} }
return 1; return 1;
} }
static int zip_file_read( static int64_t zip_file_read(
const char *path, const char *path,
const char *needle, void **buf, const char *needle, void **buf,
const char *optional_outfile) const char *optional_outfile)
{ {
file_archive_transfer_t zlib; file_archive_transfer_t state = {ARCHIVE_TRANSFER_INIT};
decomp_state_t decomp = {0};
struct archive_extract_userdata userdata = {{0}}; struct archive_extract_userdata userdata = {{0}};
bool returnerr = true; bool returnerr = true;
int ret = 0; int ret = 0;
zlib.type = ARCHIVE_TRANSFER_INIT;
zlib.archive_size = 0;
zlib.start_delta = 0;
zlib.handle = NULL;
zlib.stream = NULL;
zlib.footer = NULL;
zlib.directory = NULL;
zlib.data = NULL;
zlib.backend = NULL;
userdata.decomp_state.needle = NULL;
userdata.decomp_state.opt_file = NULL;
userdata.decomp_state.found = false;
userdata.decomp_state.buf = buf;
if (needle) if (needle)
userdata.decomp_state.needle = strdup(needle); decomp.needle = strdup(needle);
if (optional_outfile) if (optional_outfile)
userdata.decomp_state.opt_file = strdup(optional_outfile); decomp.opt_file = strdup(optional_outfile);
userdata.transfer = &state;
userdata.cb_data = &decomp;
decomp.buf = buf;
do do
{ {
ret = file_archive_parse_file_iterate(&zlib, &returnerr, path, ret = file_archive_parse_file_iterate(&state, &returnerr, path,
"", zip_file_decompressed, &userdata); "", zip_file_decompressed, &userdata);
if (!returnerr) if (!returnerr)
break; break;
} while (ret == 0 && !userdata.decomp_state.found); }while (ret == 0 && !decomp.found);
file_archive_parse_file_iterate_stop(&zlib); file_archive_parse_file_iterate_stop(&state);
if (userdata.decomp_state.opt_file) if (decomp.opt_file)
free(userdata.decomp_state.opt_file); free(decomp.opt_file);
if (userdata.decomp_state.needle) if (decomp.needle)
free(userdata.decomp_state.needle); free(decomp.needle);
if (!userdata.decomp_state.found) if (!decomp.found)
return -1; return -1;
return (int)userdata.decomp_state.size; return (int64_t)decomp.size;
} }
static int zip_parse_file_init(file_archive_transfer_t *state, static int zip_parse_file_init(file_archive_transfer_t *state,
const char *file) const char *file)
{ {
if (state->archive_size < 22) uint8_t footer_buf[1024];
uint8_t *footer = footer_buf;
int64_t read_pos = state->archive_size;
int64_t read_block = MIN(read_pos, sizeof(footer_buf));
int64_t directory_size, directory_offset;
zip_context_t *zip_context = NULL;
/* Minimal ZIP file size is 22 bytes */
if (read_block < 22)
return -1; return -1;
state->footer = state->data + state->archive_size - 22; /* Find the end of central directory record by scanning
* the file from the end towards the beginning.
*/
for (;;)
{
if (--footer < footer_buf)
{
if (read_pos <= 0)
return -1; /* reached beginning of file */
for (;; state->footer--) /* Read 21 bytes of overlaps except on the first block. */
{ if (read_pos == state->archive_size)
if (state->footer <= state->data + 22) read_pos = read_pos - read_block;
else
read_pos = MAX(read_pos - read_block + 21, 0);
/* Seek to read_pos and read read_block bytes. */
filestream_seek(state->archive_file, read_pos, RETRO_VFS_SEEK_POSITION_START);
if (filestream_read(state->archive_file, footer_buf, read_block) != read_block)
return -1; return -1;
if (read_le(state->footer, 4) == END_OF_CENTRAL_DIR_SIGNATURE)
footer = footer_buf + read_block - 22;
}
if (read_le(footer, 4) == END_OF_CENTRAL_DIR_SIGNATURE)
{ {
unsigned comment_len = read_le(state->footer + 20, 2); unsigned comment_len = read_le(footer + 20, 2);
if (state->footer + 22 + comment_len == state->data + state->archive_size) if (read_pos + (footer - footer_buf) + 22 + comment_len == state->archive_size)
break; break; /* found it! */
} }
} }
state->directory = state->data + read_le(state->footer + 16, 4); /* Read directory info and do basic sanity checks. */
directory_size = read_le(footer + 12, 4);
directory_offset = read_le(footer + 16, 4);
if (directory_size > state->archive_size
|| directory_offset > state->archive_size)
return -1;
/* This is a ZIP file, allocate one block of memory for both the
* context and the entire directory, then read the directory.
*/
zip_context = (zip_context_t*)malloc(sizeof(zip_context_t) + (size_t)directory_size);
zip_context->state = state;
zip_context->directory = (uint8_t*)(zip_context + 1);
zip_context->directory_entry = zip_context->directory;
zip_context->directory_end = zip_context->directory + (size_t)directory_size;
zip_context->current_stream = NULL;
zip_context->compressed_data = NULL;
zip_context->decompressed_data = NULL;
filestream_seek(state->archive_file, directory_offset, RETRO_VFS_SEEK_POSITION_START);
if (filestream_read(state->archive_file, zip_context->directory, directory_size) != directory_size)
{
free(zip_context);
return -1;
}
state->context = zip_context;
state->step_total = read_le(footer + 10, 2); /* total entries */;
return 0; return 0;
} }
static int zip_parse_file_iterate_step_internal( static int zip_parse_file_iterate_step_internal(
file_archive_transfer_t *state, char *filename, zip_context_t * zip_context, char *filename,
const uint8_t **cdata, const uint8_t **cdata,
unsigned *cmode, uint32_t *size, uint32_t *csize, unsigned *cmode, uint32_t *size, uint32_t *csize,
uint32_t *checksum, unsigned *payback) uint32_t *checksum, unsigned *payback)
{ {
uint32_t offset; uint8_t *entry = zip_context->directory_entry;
uint32_t namelength, extralength, commentlength, uint32_t signature, namelength, extralength, commentlength, offset;
offsetNL, offsetEL;
uint32_t signature = read_le(state->directory + 0, 4); if (entry < zip_context->directory || entry >= zip_context->directory_end)
return 0;
signature = read_le(zip_context->directory_entry + 0, 4);
if (signature != CENTRAL_FILE_HEADER_SIGNATURE) if (signature != CENTRAL_FILE_HEADER_SIGNATURE)
return 0; return 0;
*cmode = read_le(state->directory + 10, 2); /* compression mode, 0 = store, 8 = deflate */ *cmode = read_le(zip_context->directory_entry + 10, 2); /* compression mode, 0 = store, 8 = deflate */
*checksum = read_le(state->directory + 16, 4); /* CRC32 */ *checksum = read_le(zip_context->directory_entry + 16, 4); /* CRC32 */
*csize = read_le(state->directory + 20, 4); /* compressed size */ *csize = read_le(zip_context->directory_entry + 20, 4); /* compressed size */
*size = read_le(state->directory + 24, 4); /* uncompressed size */ *size = read_le(zip_context->directory_entry + 24, 4); /* uncompressed size */
namelength = read_le(state->directory + 28, 2); /* file name length */ namelength = read_le(zip_context->directory_entry + 28, 2); /* file name length */
extralength = read_le(state->directory + 30, 2); /* extra field length */ extralength = read_le(zip_context->directory_entry + 30, 2); /* extra field length */
commentlength = read_le(state->directory + 32, 2); /* file comment length */ commentlength = read_le(zip_context->directory_entry + 32, 2); /* file comment length */
if (namelength >= PATH_MAX_LENGTH) if (namelength >= PATH_MAX_LENGTH)
return -1; return -1;
memcpy(filename, state->directory + 46, namelength); /* file name */ memcpy(filename, zip_context->directory_entry + 46, namelength); /* file name */
filename[namelength] = '\0'; filename[namelength] = '\0';
offset = read_le(state->directory + 42, 4); /* relative offset of local file header */ offset = read_le(zip_context->directory_entry + 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; *cdata = (uint8_t*)(size_t)offset; /* store file offset in data pointer */
*payback = 46 + namelength + extralength + commentlength; *payback = 46 + namelength + extralength + commentlength;
return 1; return 1;
} }
static int zip_parse_file_iterate_step(file_archive_transfer_t *state, static int zip_parse_file_iterate_step(void *context,
const char *valid_exts, struct archive_extract_userdata *userdata, const char *valid_exts, struct archive_extract_userdata *userdata,
file_archive_file_cb file_cb) file_archive_file_cb file_cb)
{ {
zip_context_t *zip_context = (zip_context_t *)context;
const uint8_t *cdata = NULL; const uint8_t *cdata = NULL;
uint32_t checksum = 0; uint32_t checksum = 0;
uint32_t size = 0; uint32_t size = 0;
uint32_t csize = 0; uint32_t csize = 0;
unsigned cmode = 0; unsigned cmode = 0;
unsigned payload = 0; unsigned payload = 0;
int ret = zip_parse_file_iterate_step_internal( int ret = zip_parse_file_iterate_step_internal(zip_context,
state, userdata->current_file_path, &cdata, &cmode, &size, &csize, &checksum, &payload); userdata->current_file_path, &cdata, &cmode, &size, &csize, &checksum, &payload);
if (ret != 1) if (ret != 1)
return ret; return ret;
userdata->crc = checksum; userdata->crc = checksum;
if (file_cb && !file_cb(userdata->current_file_path, valid_exts, if (file_cb && !file_cb(userdata->current_file_path, valid_exts, cdata, cmode,
cdata, cmode,
csize, size, checksum, userdata)) csize, size, checksum, userdata))
return 0; return 0;
state->directory += payload; zip_context->directory_entry += payload;
return 1; return 1;
} }
static void zip_parse_file_free(void *context)
{
zip_context_t *zip_context = (zip_context_t *)context;
zip_context_free_stream(zip_context, false);
free(zip_context);
}
const struct file_archive_file_backend zlib_backend = { const struct file_archive_file_backend zlib_backend = {
zlib_stream_new, zip_parse_file_init,
zlib_stream_free, zip_parse_file_iterate_step,
zip_parse_file_free,
zlib_stream_decompress_data_to_file_init, zlib_stream_decompress_data_to_file_init,
zlib_stream_decompress_data_to_file_iterate, zlib_stream_decompress_data_to_file_iterate,
zlib_stream_crc32_calculate, zlib_stream_crc32_calculate,
zip_file_read, zip_file_read,
zip_parse_file_init,
zip_parse_file_iterate_step,
"zlib" "zlib"
}; };

View File

@ -39,8 +39,6 @@
RETRO_BEGIN_DECLS RETRO_BEGIN_DECLS
struct archive_extract_userdata;
enum file_archive_transfer_type enum file_archive_transfer_type
{ {
ARCHIVE_TRANSFER_NONE = 0, ARCHIVE_TRANSFER_NONE = 0,
@ -52,42 +50,24 @@ enum file_archive_transfer_type
typedef struct file_archive_handle typedef struct file_archive_handle
{ {
void *stream;
uint8_t *data; uint8_t *data;
uint32_t real_checksum; uint32_t real_checksum;
const struct file_archive_file_backend *backend;
} file_archive_file_handle_t; } file_archive_file_handle_t;
typedef struct file_archive_file_data file_archive_file_data_t;
typedef struct file_archive_transfer typedef struct file_archive_transfer
{ {
enum file_archive_transfer_type type; enum file_archive_transfer_type type;
int32_t archive_size; struct RFILE *archive_file;
ptrdiff_t start_delta; #ifdef HAVE_MMAP
file_archive_file_data_t *handle; int archive_mmap_fd;
void *stream; uint8_t *archive_mmap_data;
const uint8_t *footer; #endif
const uint8_t *directory; int64_t archive_size;
const uint8_t *data; void *context;
unsigned step_total, step_current;
const struct file_archive_file_backend *backend; const struct file_archive_file_backend *backend;
} file_archive_transfer_t; } file_archive_transfer_t;
enum file_archive_compression_mode
{
ARCHIVE_MODE_UNCOMPRESSED = 0,
ARCHIVE_MODE_COMPRESSED = 8
};
struct decomp_state_t
{
char *opt_file;
char *needle;
void **buf;
size_t size;
bool found;
};
typedef struct typedef struct
{ {
char *source_file; char *source_file;
@ -104,6 +84,7 @@ typedef struct
struct archive_extract_userdata struct archive_extract_userdata
{ {
/* These are set or read by the archive processing */
char archive_path[PATH_MAX_LENGTH]; char archive_path[PATH_MAX_LENGTH];
char current_file_path[PATH_MAX_LENGTH]; char current_file_path[PATH_MAX_LENGTH];
char *first_extracted_file_path; char *first_extracted_file_path;
@ -113,10 +94,11 @@ struct archive_extract_userdata
struct string_list *list; struct string_list *list;
bool found_file; bool found_file;
bool list_only; bool list_only;
void *context;
uint32_t crc; uint32_t crc;
struct decomp_state_t decomp_state; file_archive_transfer_t *transfer;
/* Not used by the processing, free to use outside or in iterate callback */
decompress_state_t *dec; decompress_state_t *dec;
void* cb_data;
}; };
/* Returns true when parsing should continue. False to stop. */ /* Returns true when parsing should continue. False to stop. */
@ -126,22 +108,27 @@ typedef int (*file_archive_file_cb)(const char *name, const char *valid_exts,
struct file_archive_file_backend struct file_archive_file_backend
{ {
void *(*stream_new)(void);
void (*stream_free)(void *);
bool (*stream_decompress_data_to_file_init)(
file_archive_file_handle_t *, const uint8_t *, uint32_t, uint32_t);
int (*stream_decompress_data_to_file_iterate)(void *);
uint32_t (*stream_crc_calculate)(uint32_t, const uint8_t *, size_t);
int (*compressed_file_read)(const char *path, const char *needle, void **buf,
const char *optional_outfile);
int (*archive_parse_file_init)( int (*archive_parse_file_init)(
file_archive_transfer_t *state, file_archive_transfer_t *state,
const char *file); const char *file);
int (*archive_parse_file_iterate_step)( int (*archive_parse_file_iterate_step)(
file_archive_transfer_t *state, void *context,
const char *valid_exts, const char *valid_exts,
struct archive_extract_userdata *userdata, struct archive_extract_userdata *userdata,
file_archive_file_cb file_cb); file_archive_file_cb file_cb);
void (*archive_parse_file_free)(
void *context);
bool (*stream_decompress_data_to_file_init)(
void *context, file_archive_file_handle_t *handle,
const uint8_t *cdata, unsigned cmode, uint32_t csize, uint32_t size);
int (*stream_decompress_data_to_file_iterate)(
void *context,
file_archive_file_handle_t *handle);
uint32_t (*stream_crc_calculate)(uint32_t, const uint8_t *, size_t);
int64_t (*compressed_file_read)(const char *path, const char *needle, void **buf,
const char *optional_outfile);
const char *ident; const char *ident;
}; };