diff --git a/decompress/7zip_support.c b/decompress/7zip_support.c index 7246977307..d0d88626d0 100644 --- a/decompress/7zip_support.c +++ b/decompress/7zip_support.c @@ -30,7 +30,10 @@ #include "../deps/7zip/7zFile.h" #include "../deps/7zip/7zVersion.h" - +/* Undefined at the end of the file + * Don't use outside of this file + */ +#define RARCH_ZIP_SUPPORT_BUFFER_SIZE_MAX 16384 static ISzAlloc g_Alloc = { SzAlloc, SzFree }; @@ -164,9 +167,11 @@ static SRes ConvertUtf16toCharString(const UInt16 *s, char *outstring) } /* Extract the relative path relative_path from a 7z archive - * archive_path and allocate a buf for it to write it in. */ + * archive_path and allocate a buf for it to write it in. + * If optional_outfile is set, extract to that instead and don't alloc buffer. + */ int read_7zip_file(const char * archive_path, - const char *relative_path, void **buf) + const char *relative_path, void **buf, const char* optional_outfile) { CFileInStream archiveStream; CLookToRead lookStream; @@ -245,6 +250,9 @@ int read_7zip_file(const char * archive_path, if (strcmp(infile,relative_path) == 0) { + /* 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,&blockIndex, &outBuffer, &outBufferSize,&offset, &outSizeProcessed, @@ -254,17 +262,33 @@ int read_7zip_file(const char * archive_path, break; /* This goes to the error section. */ } outsize = outSizeProcessed; - *buf = outBuffer+offset; - - /*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,outBuffer+offset,outsize); + if (optional_outfile != NULL) + { + FILE* outsink = fopen(optional_outfile,"wb"); + if (outsink == NULL) + { + RARCH_ERR("Could not open outfilepath %s in 7zip_extract.\n", + optional_outfile); + IAlloc_Free(&allocImp, outBuffer); + SzArEx_Free(&db, &allocImp); + SzFree(NULL, temp); + File_Close(&archiveStream.file); + return -1; + } + fwrite(outBuffer+offset,1,outsize,outsink); + fclose(outsink); + } + 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,outBuffer+offset,outsize); + } IAlloc_Free(&allocImp, outBuffer); break; } @@ -437,3 +461,5 @@ error: string_list_free(ext_list); return NULL; } + +#undef RARCH_ZIP_SUPPORT_BUFFER_SIZE_MAX diff --git a/decompress/7zip_support.h b/decompress/7zip_support.h index 2c425b21ea..4ada4cd333 100644 --- a/decompress/7zip_support.h +++ b/decompress/7zip_support.h @@ -21,7 +21,7 @@ extern "C" { #endif int read_7zip_file(const char * archive_path, - const char *relative_path, void **buf); + const char *relative_path, void **buf, char const* optional_outfileq); struct string_list *compressed_7zip_file_list_new(const char *path, const char* ext); diff --git a/decompress/zip_support.c b/decompress/zip_support.c index ec5b28f2de..f609a03e9a 100644 --- a/decompress/zip_support.c +++ b/decompress/zip_support.c @@ -26,15 +26,22 @@ #include "../deps/rzlib/unzip.h" +/* Undefined at the end of the file + * Don't use outside of this file + */ +#define RARCH_ZIP_SUPPORT_BUFFER_SIZE_MAX 16384 /* Extract the relative path relative_path from a * zip archive archive_path and allocate a buf for it to write it in. */ /* This code is inspired by: * stackoverflow.com/questions/10440113/simple-way-to-unzip-a-zip-file-using-zlib + * + * optional_outfile if not NULL will be used to extract the file. buf will be 0 + * then. */ int read_zip_file(const char * archive_path, - const char *relative_path, void **buf) + const char *relative_path, void **buf, const char* optional_outfile) { ssize_t bytes_read = -1; bool finished_reading = false; @@ -94,22 +101,55 @@ int read_zip_file(const char * archive_path, return -1; } - /* Allocate outbuffer */ - *buf = malloc(file_info.uncompressed_size + 1 ); - - bytes_read = unzReadCurrentFile( zipfile, *buf, file_info.uncompressed_size ); - if (bytes_read != file_info.uncompressed_size) + if (optional_outfile != 0) { - RARCH_ERR( + char read_buffer[RARCH_ZIP_SUPPORT_BUFFER_SIZE_MAX]; + FILE* outsink = fopen(optional_outfile,"wb"); + if (outsink == NULL) + { + RARCH_ERR("Could not open outfilepath %s in zip_extract.\n", + optional_outfile); + unzCloseCurrentFile( zipfile ); + unzClose( zipfile ); + return -1; + } + bytes_read = 0; + do + { + bytes_read = unzReadCurrentFile( zipfile, read_buffer, + RARCH_ZIP_SUPPORT_BUFFER_SIZE_MAX ); + ssize_t fwrite_bytes = fwrite(read_buffer,1,bytes_read,outsink); + if (fwrite_bytes != bytes_read) + { + /* couldn't write all bytes */ + RARCH_ERR("Error writing to %s.\n",optional_outfile); + fclose(outsink); + unzCloseCurrentFile( zipfile ); + unzClose( zipfile ); + return -1; + } + } while(bytes_read > 0) ; + fclose(outsink); + } + else + { + /* Allocate outbuffer */ + *buf = malloc(file_info.uncompressed_size + 1 ); + + bytes_read = unzReadCurrentFile( zipfile, *buf, file_info.uncompressed_size ); + if (bytes_read != file_info.uncompressed_size) + { + RARCH_ERR( "We tried to read %d bytes, but only got %d of file %s in zip %s.\n", (unsigned int) file_info.uncompressed_size, (int)bytes_read, relative_path, archive_path); - free(*buf); - unzCloseCurrentFile( zipfile ); - unzClose( zipfile ); - return -1; + free(*buf); + unzCloseCurrentFile( zipfile ); + unzClose( zipfile ); + return -1; + } + ((char*)(*buf))[file_info.uncompressed_size] = '\0'; } - ((char*)(*buf))[file_info.uncompressed_size] = '\0'; finished_reading = true; } unzCloseCurrentFile( zipfile ); @@ -238,3 +278,5 @@ struct string_list *compressed_zip_file_list_new(const char *path, unzClose( zipfile ); return list; } + +#undef RARCH_ZIP_SUPPORT_BUFFER_SIZE_MAX diff --git a/decompress/zip_support.h b/decompress/zip_support.h index c11d88e891..ede1cb6fef 100644 --- a/decompress/zip_support.h +++ b/decompress/zip_support.h @@ -21,7 +21,7 @@ extern "C" { #endif int read_zip_file(const char * archive_path, - const char *relative_path, void **buf); + const char *relative_path, void **buf, const char* optional_outfile); struct string_list *compressed_zip_file_list_new(const char *path, const char* ext); diff --git a/file.c b/file.c index 8264902c82..c4503629bd 100644 --- a/file.c +++ b/file.c @@ -398,21 +398,12 @@ static bool load_content(const struct retro_subsystem_info *special, "extraction directory was not set or found. Exiting.\n"); rarch_assert(false); } - /* This is a test implementation, currently it needs as much - * RAM as the file is big. - */ + char new_path[PATH_MAX]; fill_pathname_join(new_path,g_settings.extraction_directory, path_basename(path),sizeof(new_path)); - void *buf = NULL; - ssize_t bytes_read = -1; - bytes_read = read_file(path, &buf); - write_file(new_path,buf,bytes_read); - - info[i].path =strdup(new_path); - if(buf) - free(buf); - buf = NULL; + read_compressed_file(path,NULL,new_path); + info[i].path = strdup(new_path); } } } diff --git a/file_path.c b/file_path.c index b9f505075b..729c778bb4 100644 --- a/file_path.c +++ b/file_path.c @@ -92,10 +92,22 @@ bool write_empty_file(const char *path) return true; } -/* Generic compressed file loader. */ +/* Generic compressed file loader. + * Extracts to buf, unless optional_filename != 0 + * Then extracts to optional_filename and leaves buf alone. + */ #ifdef HAVE_COMPRESSION -long read_compressed_file(const char * path, void **buf) +long read_compressed_file(const char * path, void **buf, + const char* optional_filename) { + /* 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) + if(path_file_exists(optional_filename)) + return 0; + //We split carchive path and relative path: char archive_path[PATH_MAX]; strlcpy(archive_path,path,sizeof(archive_path)); @@ -123,11 +135,11 @@ long read_compressed_file(const char * path, void **buf) const char* file_ext = path_get_extension(archive_path); #ifdef HAVE_7ZIP if (strcasecmp(file_ext,"7z") == 0) - return read_7zip_file(archive_path,archive_found,buf); + return read_7zip_file(archive_path,archive_found,buf,optional_filename); #endif #ifdef HAVE_ZLIB if (strcasecmp(file_ext,"zip") == 0) - return read_zip_file(archive_path,archive_found,buf); + return read_zip_file(archive_path,archive_found,buf,optional_filename); #endif return -1; } @@ -187,7 +199,7 @@ long read_file(const char *path, void **buf) * */ #ifdef HAVE_COMPRESSION if (path_contains_compressed_file(path)) - return read_compressed_file(path,buf); + return read_compressed_file(path,buf,0); #endif return read_generic_file(path,buf); } diff --git a/file_path.h b/file_path.h index fda31adf29..6277300454 100644 --- a/file_path.h +++ b/file_path.h @@ -43,7 +43,8 @@ enum #ifdef HAVE_COMPRESSION -long read_compressed_file(const char * path, void **buf); +long read_compressed_file(const char * path, void **buf, + const char* optional_filename); #endif long read_file(const char *path, void **buf);