diff --git a/CMakeLists.txt b/CMakeLists.txt index 7bdaf78d4..0a69bc41b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,7 @@ set(USE_GDB_STUB ON CACHE BOOL "Whether or not to enable the GDB stub ARM debugg set(USE_FFMPEG ON CACHE BOOL "Whether or not to enable FFmpeg support") set(USE_ZLIB ON CACHE BOOL "Whether or not to enable zlib support") set(USE_PNG ON CACHE BOOL "Whether or not to enable PNG support") -set(USE_LIBZIP ON CACHE BOOL "Whether or not to enable ZIP support") +set(USE_LIBZIP ON CACHE BOOL "Whether or not to enable LIBZIP support") set(USE_MAGICK ON CACHE BOOL "Whether or not to enable ImageMagick support") set(USE_BLIP ON CACHE BOOL "Whether or not to enable blip_buf support") set(USE_LZMA ON CACHE BOOL "Whether or not to enable 7-Zip support") @@ -408,6 +408,10 @@ if(USE_LIBZIP) list(APPEND FEATURES LIBZIP) list(APPEND VFS_SRC ${CMAKE_SOURCE_DIR}/src/util/vfs/vfs-zip.c) set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libzip2") +elseif(USE_ZLIB) + list(APPEND VFS_SRC ${CMAKE_SOURCE_DIR}/src/util/vfs/vfs-zip.c + ${CMAKE_SOURCE_DIR}/src/third-party/zlib/contrib/minizip/ioapi.c + ${CMAKE_SOURCE_DIR}/src/third-party/zlib/contrib/minizip/unzip.c) endif() if (USE_LZMA) @@ -602,7 +606,6 @@ message(STATUS " GDB stub: ${USE_GDB_STUB}") message(STATUS " Video recording: ${USE_FFMPEG}") message(STATUS " GIF recording: ${USE_MAGICK}") message(STATUS " Screenshot/advanced savestate support: ${USE_PNG}") -message(STATUS " ZIP support: ${USE_LIBZIP}") message(STATUS " 7-Zip support: ${USE_LZMA}") message(STATUS " Better audio resampling: ${USE_BLIP}") message(STATUS "Frontend summary:") diff --git a/src/util/vfs.c b/src/util/vfs.c index 3f6adb9cb..da098dfe3 100644 --- a/src/util/vfs.c +++ b/src/util/vfs.c @@ -98,7 +98,7 @@ struct VFile* VFileOpen(const char* path, int flags) { struct VDir* VDirOpenArchive(const char* path) { struct VDir* dir = 0; -#if USE_LIBZIP +#if defined(USE_LIBZIP) || defined(USE_ZLIB) if (!dir) { dir = VDirOpenZip(path, 0); } diff --git a/src/util/vfs.h b/src/util/vfs.h index 8f89e32cf..7ab637881 100644 --- a/src/util/vfs.h +++ b/src/util/vfs.h @@ -72,7 +72,7 @@ struct VFile* VFileFromFILE(FILE* file); struct VDir* VDirOpen(const char* path); struct VDir* VDirOpenArchive(const char* path); -#ifdef USE_LIBZIP +#if defined(USE_LIBZIP) || defined(USE_ZLIB) struct VDir* VDirOpenZip(const char* path, int flags); #endif diff --git a/src/util/vfs/vfs-zip.c b/src/util/vfs/vfs-zip.c index 3c37b125a..4b122c785 100644 --- a/src/util/vfs/vfs-zip.c +++ b/src/util/vfs/vfs-zip.c @@ -8,11 +8,6 @@ #ifdef USE_LIBZIP #include - -enum { - BLOCK_SIZE = 1024 -}; - struct VDirEntryZip { struct VDirEntry d; struct zip* z; @@ -35,6 +30,35 @@ struct VFileZip { size_t fileSize; }; +enum { + BLOCK_SIZE = 1024 +}; +#else +#include "third-party/zlib/contrib/minizip/unzip.h" +#include "util/memory.h" + +struct VDirEntryZip { + struct VDirEntry d; + char name[PATH_MAX]; + size_t fileSize; + unzFile z; +}; + +struct VDirZip { + struct VDir d; + unzFile z; + struct VDirEntryZip dirent; + bool hasNextFile; +}; + +struct VFileZip { + struct VFile d; + unzFile z; + void* buffer; + size_t fileSize; +}; +#endif + static bool _vfzClose(struct VFile* vf); static off_t _vfzSeek(struct VFile* vf, off_t offset, int whence); static ssize_t _vfzRead(struct VFile* vf, void* buffer, size_t size); @@ -55,6 +79,13 @@ static const char* _vdezName(struct VDirEntry* vde); static enum VFSType _vdezType(struct VDirEntry* vde); struct VDir* VDirOpenZip(const char* path, int flags) { +#ifndef USE_LIBZIP + UNUSED(flags); + unzFile z = unzOpen(path); + if (!z) { + return 0; + } +#else int zflags = 0; if (flags & O_CREAT) { zflags |= ZIP_CREATE; @@ -67,6 +98,7 @@ struct VDir* VDirOpenZip(const char* path, int flags) { if (!z) { return 0; } +#endif struct VDirZip* vd = malloc(sizeof(struct VDirZip)); vd->d.close = _vdzClose; @@ -76,14 +108,21 @@ struct VDir* VDirOpenZip(const char* path, int flags) { vd->d.openDir = _vdzOpenDir; vd->z = z; +#ifndef USE_LIBZIP + vd->hasNextFile = true; +#endif + vd->dirent.d.name = _vdezName; vd->dirent.d.type = _vdezType; +#ifdef USE_LIBZIP vd->dirent.index = -1; +#endif vd->dirent.z = z; return &vd->d; } +#ifdef USE_LIBZIP bool _vfzClose(struct VFile* vf) { struct VFileZip* vfz = (struct VFileZip*) vf; if (zip_fclose(vfz->zf) < 0) { @@ -326,5 +365,212 @@ static enum VFSType _vdezType(struct VDirEntry* vde) { UNUSED(vdez); return VFS_UNKNOWN; } +#else +bool _vfzClose(struct VFile* vf) { + struct VFileZip* vfz = (struct VFileZip*) vf; + unzCloseCurrentFile(vfz->z); + free(vfz->buffer); + free(vfz); + return true; +} +off_t _vfzSeek(struct VFile* vf, off_t offset, int whence) { + struct VFileZip* vfz = (struct VFileZip*) vf; + + int64_t currentPos = unztell64(vfz->z); + int64_t pos; + switch (whence) { + case SEEK_SET: + pos = 0; + break; + case SEEK_CUR: + pos = unztell64(vfz->z); + break; + case SEEK_END: + pos = vfz->fileSize; + break; + } + + if (pos < 0 || pos + offset < 0) { + return -1; + } + pos += offset; + if (currentPos > pos) { + unzCloseCurrentFile(vfz->z); + unzOpenCurrentFile(vfz->z); + currentPos = 0; + } + while (currentPos < pos) { + char tempBuf[1024]; + ssize_t toRead = sizeof(tempBuf); + if (toRead > pos - currentPos) { + toRead = pos - currentPos; + } + ssize_t read = vf->read(vf, tempBuf, toRead); + if (read < toRead) { + return -1; + } + currentPos += read; + } + + return unztell64(vfz->z); +} + +ssize_t _vfzRead(struct VFile* vf, void* buffer, size_t size) { + struct VFileZip* vfz = (struct VFileZip*) vf; + return unzReadCurrentFile(vfz->z, buffer, size); +} + +ssize_t _vfzWrite(struct VFile* vf, const void* buffer, size_t size) { + // TODO + UNUSED(vf); + UNUSED(buffer); + UNUSED(size); + return -1; +} + +void* _vfzMap(struct VFile* vf, size_t size, int flags) { + struct VFileZip* vfz = (struct VFileZip*) vf; + + // TODO + UNUSED(flags); + + off_t pos = vf->seek(vf, 0, SEEK_CUR); + if (pos < 0) { + return 0; + } + + vfz->buffer = anonymousMemoryMap(size); + if (!vfz->buffer) { + return 0; + } + + unzCloseCurrentFile(vfz->z); + unzOpenCurrentFile(vfz->z); + vf->read(vf, vfz->buffer, size); + unzCloseCurrentFile(vfz->z); + unzOpenCurrentFile(vfz->z); + vf->seek(vf, pos, SEEK_SET); + + return vfz->buffer; +} + +void _vfzUnmap(struct VFile* vf, void* memory, size_t size) { + struct VFileZip* vfz = (struct VFileZip*) vf; + + if (memory != vfz->buffer) { + return; + } + + mappedMemoryFree(vfz->buffer, size); + vfz->buffer = 0; +} + +void _vfzTruncate(struct VFile* vf, size_t size) { + // TODO + UNUSED(vf); + UNUSED(size); +} + +ssize_t _vfzSize(struct VFile* vf) { + struct VFileZip* vfz = (struct VFileZip*) vf; + return vfz->fileSize; +} + +bool _vdzClose(struct VDir* vd) { + struct VDirZip* vdz = (struct VDirZip*) vd; + if (unzClose(vdz->z) < 0) { + return false; + } + free(vdz); + return true; +} + +void _vdzRewind(struct VDir* vd) { + struct VDirZip* vdz = (struct VDirZip*) vd; + vdz->hasNextFile = unzGoToFirstFile(vdz->z) == UNZ_OK; +} + +struct VDirEntry* _vdzListNext(struct VDir* vd) { + struct VDirZip* vdz = (struct VDirZip*) vd; + if (!vdz->hasNextFile) { + return 0; + } + unz_file_info64 info; + int status = unzGetCurrentFileInfo64(vdz->z, &info, vdz->dirent.name, sizeof(vdz->dirent.name), 0, 0, 0, 0); + if (status < 0) { + return 0; + } + vdz->dirent.fileSize = info.uncompressed_size; + if (unzGoToNextFile(vdz->z) == UNZ_END_OF_LIST_OF_FILE) { + vdz->hasNextFile = false; + } + return &vdz->dirent.d; +} + +struct VFile* _vdzOpenFile(struct VDir* vd, const char* path, int mode) { + UNUSED(mode); + struct VDirZip* vdz = (struct VDirZip*) vd; + + if ((mode & O_ACCMODE) != O_RDONLY) { + // minizip implementation only supports read + return 0; + } + + if (unzLocateFile(vdz->z, path, 0) != UNZ_OK) { + return 0; + } + + if (unzOpenCurrentFile(vdz->z) < 0) { + return 0; + } + + unz_file_info64 info; + int status = unzGetCurrentFileInfo64(vdz->z, &info, 0, 0, 0, 0, 0, 0); + if (status < 0) { + return 0; + } + + struct VFileZip* vfz = malloc(sizeof(struct VFileZip)); + vfz->z = vdz->z; + vfz->buffer = 0; + vfz->fileSize = info.uncompressed_size; + + vfz->d.close = _vfzClose; + vfz->d.seek = _vfzSeek; + vfz->d.read = _vfzRead; + vfz->d.readline = VFileReadline; + vfz->d.write = _vfzWrite; + vfz->d.map = _vfzMap; + vfz->d.unmap = _vfzUnmap; + vfz->d.truncate = _vfzTruncate; + vfz->d.size = _vfzSize; + vfz->d.sync = _vfzSync; + + return &vfz->d; +} + +struct VDir* _vdzOpenDir(struct VDir* vd, const char* path) { + UNUSED(vd); + UNUSED(path); + return 0; +} + +bool _vfzSync(struct VFile* vf, const void* memory, size_t size) { + UNUSED(vf); + UNUSED(memory); + UNUSED(size); + return false; +} + +const char* _vdezName(struct VDirEntry* vde) { + struct VDirEntryZip* vdez = (struct VDirEntryZip*) vde; + return vdez->name; +} + +static enum VFSType _vdezType(struct VDirEntry* vde) { + struct VDirEntryZip* vdez = (struct VDirEntryZip*) vde; + UNUSED(vdez); + return VFS_UNKNOWN; +} #endif