diff options
Diffstat (limited to 'src/3rdparty/assimp/contrib/zip/src/zip.c')
-rw-r--r-- | src/3rdparty/assimp/contrib/zip/src/zip.c | 640 |
1 files changed, 640 insertions, 0 deletions
diff --git a/src/3rdparty/assimp/contrib/zip/src/zip.c b/src/3rdparty/assimp/contrib/zip/src/zip.c new file mode 100644 index 000000000..80573096b --- /dev/null +++ b/src/3rdparty/assimp/contrib/zip/src/zip.c @@ -0,0 +1,640 @@ +/* + * 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 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 "zip.h" +#include "miniz.h" + +#include <errno.h> +#include <sys/stat.h> +#include <time.h> + +#if defined _WIN32 || defined __WIN32__ +/* Win32, DOS */ +#include <direct.h> + +#define MKDIR(DIRNAME) _mkdir(DIRNAME) +#define STRCLONE(STR) ((STR) ? _strdup(STR) : NULL) +#define HAS_DEVICE(P) \ + ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) && \ + (P)[1] == ':') +#define FILESYSTEM_PREFIX_LEN(P) (HAS_DEVICE(P) ? 2 : 0) +#define ISSLASH(C) ((C) == '/' || (C) == '\\') + +#else +#define MKDIR(DIRNAME) mkdir(DIRNAME, 0755) +#define STRCLONE(STR) ((STR) ? strdup(STR) : NULL) +#endif + +#ifndef FILESYSTEM_PREFIX_LEN +#define FILESYSTEM_PREFIX_LEN(P) 0 +#endif + +#ifndef ISSLASH +#define ISSLASH(C) ((C) == '/') +#endif + +#define CLEANUP(ptr) \ + do { \ + if (ptr) { \ + free((void *)ptr); \ + ptr = NULL; \ + } \ + } while (0) + +static char *basename(const char *name) { + char const *p; + char const *base = name += FILESYSTEM_PREFIX_LEN(name); + int all_slashes = 1; + + for (p = name; *p; p++) { + if (ISSLASH(*p)) + base = p + 1; + else + all_slashes = 0; + } + + /* If NAME is all slashes, arrange to return `/'. */ + if (*base == '\0' && ISSLASH(*name) && all_slashes) --base; + + return (char *)base; +} + +static int mkpath(const char *path) { + char const *p; + char npath[MAX_PATH + 1] = {0}; + int len = 0; + + for (p = path; *p && len < MAX_PATH; p++) { + if (ISSLASH(*p) && len > 0) { + if (MKDIR(npath) == -1) + if (errno != EEXIST) return -1; + } + npath[len++] = *p; + } + + return 0; +} + +static char *strrpl(const char *str, char oldchar, char newchar) { + char *rpl = (char *)malloc(sizeof(char) * (1 + strlen(str))); + char *begin = rpl; + char c; + while((c = *str++)) { + if (c == oldchar) { + c = newchar; + } + *rpl++ = c; + } + *rpl = '\0'; + + return begin; +} + +struct zip_entry_t { + int index; + const char *name; + mz_uint64 uncomp_size; + mz_uint64 comp_size; + mz_uint32 uncomp_crc32; + mz_uint64 offset; + mz_uint8 header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; + mz_uint64 header_offset; + mz_uint16 method; + mz_zip_writer_add_state state; + tdefl_compressor comp; +}; + +struct zip_t { + mz_zip_archive archive; + mz_uint level; + struct zip_entry_t entry; + char mode; +}; + +struct zip_t *zip_open(const char *zipname, int level, char mode) { + struct zip_t *zip = NULL; + + if (!zipname || strlen(zipname) < 1) { + // zip_t archive name is empty or NULL + goto cleanup; + } + + if (level < 0) level = MZ_DEFAULT_LEVEL; + if ((level & 0xF) > MZ_UBER_COMPRESSION) { + // Wrong compression level + goto cleanup; + } + + zip = (struct zip_t *)calloc((size_t)1, sizeof(struct zip_t)); + if (!zip) goto cleanup; + + zip->level = level; + zip->mode = mode; + switch (mode) { + case 'w': + // Create a new archive. + if (!mz_zip_writer_init_file(&(zip->archive), zipname, 0)) { + // Cannot initialize zip_archive writer + goto cleanup; + } + break; + + case 'r': + case 'a': + if (!mz_zip_reader_init_file( + &(zip->archive), zipname, + level | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) { + // An archive file does not exist or cannot initialize + // zip_archive reader + goto cleanup; + } + + if (mode == 'a' && + !mz_zip_writer_init_from_reader(&(zip->archive), zipname)) { + mz_zip_reader_end(&(zip->archive)); + goto cleanup; + } + + break; + + default: + goto cleanup; + } + + return zip; + +cleanup: + CLEANUP(zip); + return NULL; +} + +void zip_close(struct zip_t *zip) { + if (zip) { + // Always finalize, even if adding failed for some reason, so we have a + // valid central directory. + mz_zip_writer_finalize_archive(&(zip->archive)); + + mz_zip_writer_end(&(zip->archive)); + mz_zip_reader_end(&(zip->archive)); + + CLEANUP(zip); + } +} + +int zip_entry_open(struct zip_t *zip, const char *entryname) { + char *locname = NULL; + size_t entrylen = 0; + mz_zip_archive *pzip = NULL; + mz_uint num_alignment_padding_bytes, level; + + if (!zip || !entryname) { + return -1; + } + + entrylen = strlen(entryname); + if (entrylen < 1) { + return -1; + } + + pzip = &(zip->archive); + /* + .ZIP File Format Specification Version: 6.3.3 + + 4.4.17.1 The name of the file, with optional relative path. + The path stored MUST not contain a drive or + device letter, or a leading slash. All slashes + MUST be forward slashes '/' as opposed to + backwards slashes '\' for compatibility with Amiga + and UNIX file systems etc. If input came from standard + input, there is no file name field. + */ + locname = strrpl(entryname, '\\', '/'); + + if (zip->mode == 'r') { + zip->entry.index = mz_zip_reader_locate_file(pzip, locname, NULL, 0); + CLEANUP(locname); + return (zip->entry.index < 0) ? -1 : 0; + } + + zip->entry.index = zip->archive.m_total_files; + zip->entry.name = locname; + if (!zip->entry.name) { + // Cannot parse zip entry name + return -1; + } + + zip->entry.comp_size = 0; + zip->entry.uncomp_size = 0; + zip->entry.uncomp_crc32 = MZ_CRC32_INIT; + zip->entry.offset = zip->archive.m_archive_size; + zip->entry.header_offset = zip->archive.m_archive_size; + memset(zip->entry.header, 0, + MZ_ZIP_LOCAL_DIR_HEADER_SIZE * sizeof(mz_uint8)); + zip->entry.method = 0; + + num_alignment_padding_bytes = + mz_zip_writer_compute_padding_needed_for_file_alignment(pzip); + + if (!pzip->m_pState || (pzip->m_zip_mode != MZ_ZIP_MODE_WRITING)) { + // Wrong zip mode + return -1; + } + if (zip->level & MZ_ZIP_FLAG_COMPRESSED_DATA) { + // Wrong zip compression level + return -1; + } + // no zip64 support yet + if ((pzip->m_total_files == 0xFFFF) || + ((pzip->m_archive_size + num_alignment_padding_bytes + + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + + entrylen) > 0xFFFFFFFF)) { + // No zip64 support yet + return -1; + } + if (!mz_zip_writer_write_zeros( + pzip, zip->entry.offset, + num_alignment_padding_bytes + sizeof(zip->entry.header))) { + // Cannot memset zip entry header + return -1; + } + + zip->entry.header_offset += num_alignment_padding_bytes; + if (pzip->m_file_offset_alignment) { + MZ_ASSERT((zip->entry.header_offset & + (pzip->m_file_offset_alignment - 1)) == 0); + } + zip->entry.offset += + num_alignment_padding_bytes + sizeof(zip->entry.header); + + if (pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.offset, zip->entry.name, + entrylen) != entrylen) { + // Cannot write data to zip entry + return -1; + } + + zip->entry.offset += entrylen; + level = zip->level & 0xF; + if (level) { + zip->entry.state.m_pZip = pzip; + zip->entry.state.m_cur_archive_file_ofs = zip->entry.offset; + zip->entry.state.m_comp_size = 0; + + if (tdefl_init(&(zip->entry.comp), mz_zip_writer_add_put_buf_callback, + &(zip->entry.state), + tdefl_create_comp_flags_from_zip_params( + level, -15, MZ_DEFAULT_STRATEGY)) != + TDEFL_STATUS_OKAY) { + // Cannot initialize the zip compressor + return -1; + } + } + + return 0; +} + +int zip_entry_close(struct zip_t *zip) { + mz_zip_archive *pzip = NULL; + mz_uint level; + tdefl_status done; + mz_uint16 entrylen; + time_t t; + struct tm *tm; + mz_uint16 dos_time, dos_date; + int status = -1; + + if (!zip) { + // zip_t handler is not initialized + return -1; + } + + if (zip->mode == 'r') { + return 0; + } + + pzip = &(zip->archive); + level = zip->level & 0xF; + if (level) { + done = tdefl_compress_buffer(&(zip->entry.comp), "", 0, TDEFL_FINISH); + if (done != TDEFL_STATUS_DONE && done != TDEFL_STATUS_OKAY) { + // Cannot flush compressed buffer + goto cleanup; + } + zip->entry.comp_size = zip->entry.state.m_comp_size; + zip->entry.offset = zip->entry.state.m_cur_archive_file_ofs; + zip->entry.method = MZ_DEFLATED; + } + + entrylen = (mz_uint16)strlen(zip->entry.name); + t = time(NULL); + tm = localtime(&t); + dos_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + + ((tm->tm_sec) >> 1)); + dos_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) + + ((tm->tm_mon + 1) << 5) + tm->tm_mday); + + // no zip64 support yet + if ((zip->entry.comp_size > 0xFFFFFFFF) || + (zip->entry.offset > 0xFFFFFFFF)) { + // No zip64 support, yet + goto cleanup; + } + + if (!mz_zip_writer_create_local_dir_header( + pzip, zip->entry.header, entrylen, 0, zip->entry.uncomp_size, + zip->entry.comp_size, zip->entry.uncomp_crc32, zip->entry.method, 0, + dos_time, dos_date)) { + // Cannot create zip entry header + goto cleanup; + } + + if (pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.header_offset, + zip->entry.header, sizeof(zip->entry.header)) != + sizeof(zip->entry.header)) { + // Cannot write zip entry header + goto cleanup; + } + + if (!mz_zip_writer_add_to_central_dir( + pzip, zip->entry.name, entrylen, NULL, 0, "", 0, + zip->entry.uncomp_size, zip->entry.comp_size, + zip->entry.uncomp_crc32, zip->entry.method, 0, dos_time, dos_date, + zip->entry.header_offset, 0)) { + // Cannot write to zip central dir + goto cleanup; + } + + pzip->m_total_files++; + pzip->m_archive_size = zip->entry.offset; + status = 0; + +cleanup: + CLEANUP(zip->entry.name); + return status; +} + +int zip_entry_write(struct zip_t *zip, const void *buf, size_t bufsize) { + mz_uint level; + mz_zip_archive *pzip = NULL; + tdefl_status status; + + if (!zip) { + // zip_t handler is not initialized + return -1; + } + + pzip = &(zip->archive); + if (buf && bufsize > 0) { + zip->entry.uncomp_size += bufsize; + zip->entry.uncomp_crc32 = (mz_uint32)mz_crc32( + zip->entry.uncomp_crc32, (const mz_uint8 *)buf, bufsize); + + level = zip->level & 0xF; + if (!level) { + if ((pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.offset, buf, + bufsize) != bufsize)) { + // Cannot write buffer + return -1; + } + zip->entry.offset += bufsize; + zip->entry.comp_size += bufsize; + } else { + status = tdefl_compress_buffer(&(zip->entry.comp), buf, bufsize, + TDEFL_NO_FLUSH); + if (status != TDEFL_STATUS_DONE && status != TDEFL_STATUS_OKAY) { + // Cannot compress buffer + return -1; + } + } + } + + return 0; +} + +int zip_entry_fwrite(struct zip_t *zip, const char *filename) { + int status = 0; + size_t n = 0; + FILE *stream = NULL; + mz_uint8 buf[MZ_ZIP_MAX_IO_BUF_SIZE] = {0}; + + if (!zip) { + // zip_t handler is not initialized + return -1; + } + + stream = fopen(filename, "rb"); + if (!stream) { + // Cannot open filename + return -1; + } + + while ((n = fread(buf, sizeof(mz_uint8), MZ_ZIP_MAX_IO_BUF_SIZE, stream)) > + 0) { + if (zip_entry_write(zip, buf, n) < 0) { + status = -1; + break; + } + } + fclose(stream); + + return status; +} + +int zip_entry_read(struct zip_t *zip, void **buf, size_t *bufsize) { + mz_zip_archive *pzip = NULL; + mz_uint idx; + + if (!zip) { + // zip_t handler is not initialized + return -1; + } + + if (zip->mode != 'r' || zip->entry.index < 0) { + // the entry is not found or we do not have read access + return -1; + } + + pzip = &(zip->archive); + idx = (mz_uint)zip->entry.index; + if (mz_zip_reader_is_file_a_directory(pzip, idx)) { + // the entry is a directory + return -1; + } + + *buf = mz_zip_reader_extract_to_heap(pzip, idx, bufsize, 0); + return (*buf) ? 0 : -1; +} + +int zip_entry_fread(struct zip_t *zip, const char *filename) { + mz_zip_archive *pzip = NULL; + mz_uint idx; + + if (!zip) { + // zip_t handler is not initialized + return -1; + } + + if (zip->mode != 'r' || zip->entry.index < 0) { + // the entry is not found or we do not have read access + return -1; + } + + pzip = &(zip->archive); + idx = (mz_uint)zip->entry.index; + if (mz_zip_reader_is_file_a_directory(pzip, idx)) { + // the entry is a directory + return -1; + } + + return (mz_zip_reader_extract_to_file(pzip, idx, filename, 0)) ? 0 : -1; +} + +int zip_entry_extract(struct zip_t *zip, + size_t (*on_extract)(void *arg, unsigned long long offset, + const void *buf, size_t bufsize), + void *arg) { + mz_zip_archive *pzip = NULL; + mz_uint idx; + + if (!zip) { + // zip_t handler is not initialized + return -1; + } + + if (zip->mode != 'r' || zip->entry.index < 0) { + // the entry is not found or we do not have read access + return -1; + } + + pzip = &(zip->archive); + idx = (mz_uint)zip->entry.index; + + return (mz_zip_reader_extract_to_callback(pzip, idx, on_extract, arg, 0)) + ? 0 + : -1; +} + +int zip_create(const char *zipname, const char *filenames[], size_t len) { + int status = 0; + size_t i; + mz_zip_archive zip_archive; + + if (!zipname || strlen(zipname) < 1) { + // zip_t archive name is empty or NULL + return -1; + } + + // Create a new archive. + if (!memset(&(zip_archive), 0, sizeof(zip_archive))) { + // Cannot memset zip archive + return -1; + } + + if (!mz_zip_writer_init_file(&zip_archive, zipname, 0)) { + // Cannot initialize zip_archive writer + return -1; + } + + for (i = 0; i < len; ++i) { + const char *name = filenames[i]; + if (!name) { + status = -1; + break; + } + + if (!mz_zip_writer_add_file(&zip_archive, basename(name), name, "", 0, + ZIP_DEFAULT_COMPRESSION_LEVEL)) { + // Cannot add file to zip_archive + status = -1; + break; + } + } + + mz_zip_writer_finalize_archive(&zip_archive); + mz_zip_writer_end(&zip_archive); + return status; +} + +int zip_extract(const char *zipname, const char *dir, + int (*on_extract)(const char *filename, void *arg), void *arg) { + int status = -1; + mz_uint i, n; + char path[MAX_PATH + 1] = {0}; + mz_zip_archive zip_archive; + mz_zip_archive_file_stat info; + size_t dirlen = 0; + + if (!memset(&(zip_archive), 0, sizeof(zip_archive))) { + // Cannot memset zip archive + return -1; + } + + if (!zipname || !dir) { + // Cannot parse zip archive name + return -1; + } + + dirlen = strlen(dir); + if (dirlen + 1 > MAX_PATH) { + return -1; + } + + // Now try to open the archive. + if (!mz_zip_reader_init_file(&zip_archive, zipname, 0)) { + // Cannot initialize zip_archive reader + return -1; + } + + strcpy(path, dir); + if (!ISSLASH(path[dirlen - 1])) { +#if defined _WIN32 || defined __WIN32__ + path[dirlen] = '\\'; +#else + path[dirlen] = '/'; +#endif + ++dirlen; + } + + // Get and print information about each file in the archive. + n = mz_zip_reader_get_num_files(&zip_archive); + for (i = 0; i < n; ++i) { + if (!mz_zip_reader_file_stat(&zip_archive, i, &info)) { + // Cannot get information about zip archive; + goto out; + } + strncpy(&path[dirlen], info.m_filename, MAX_PATH - dirlen); + if (mkpath(path) < 0) { + // Cannot make a path + goto out; + } + + if (!mz_zip_reader_is_file_a_directory(&zip_archive, i)) { + if (!mz_zip_reader_extract_to_file(&zip_archive, i, path, 0)) { + // Cannot extract zip archive to file + goto out; + } + } + + if (on_extract) { + if (on_extract(path, arg) < 0) { + goto out; + } + } + } + status = 0; + +out: + // Close the archive, freeing any resources it was using + if (!mz_zip_reader_end(&zip_archive)) { + // Cannot end zip reader + status = -1; + } + + return status; +} |