/*------------------------------------------------------------------------------ GSFramework Copyright 2001-2013 Emmanuel Julien. All Rights Reserved. ----------------------------------------------------------------------------- */ #include "io_zip/io_zip.h" #include "unzip.h" #include "filesystem/io_handle_segment.h" #include "filesystem/filesystem.h" #include "platform.h" #include "log/log.h" using namespace GS::IO; //------------------------------------------------------------------------------ // I/O wrapper for unzip (allows archive access from various I/O systems). static voidpf Zip_open_file_func(voidpf opaque, const char *filename, int mode) { __LOG__ << "ZipOpenFileFunc(); filename: " << filename << ", mode: " << mode << ".\n"; if (mode & ZLIB_FILEFUNC_MODE_READ) return GS::Platform::Get().io->Open(filename); if (mode & ZLIB_FILEFUNC_MODE_WRITE) return GS::Platform::Get().io->Open(filename, ModeWrite); return NULL; } static uLong Zip_read_file_func(voidpf opaque, voidpf stream, void *buf, uLong size) { return ((Handle *)stream)->Read(buf, size); } static uLong Zip_write_file_func(voidpf opaque, voidpf stream, const void *buf, uLong size) { return ((Handle *)stream)->Write(buf, size); } static int Zip_close_file_func(voidpf opaque, voidpf stream) { __LOG__ << "ZipCloseFileFunc();\n"; delete ((Handle *)stream); return UNZ_OK; } static int Zip_testerror_file_func(voidpf opaque, voidpf stream) { return UNZ_OK; } static long Zip_tell_file_func(voidpf opaque, voidpf stream) { return ((Handle *)stream)->Tell(); } static long Zip_seek_file_func(voidpf opaque, voidpf stream, uLong offset, int origin) { static Base::SeekRef ref[] = { Base::SeekStart, Base::SeekCurrent, Base::SeekEnd }; return ((Handle *)stream)->Seek(offset, ref[origin]) == -1 ? -1 : 0; } //------------------------------------------------------------------------------ static zlib_filefunc_def Zip_filefunc = { Zip_open_file_func, Zip_read_file_func, Zip_write_file_func, Zip_tell_file_func, Zip_seek_file_func, Zip_close_file_func, Zip_testerror_file_func, 0 }; //------------------------------------------------------------------------------ bool Zip::SetArchive(const char *uri, const char *pass) { if (zfile) unzClose(zfile); zfile = NULL; if (!uri) return true; __LOG_H__ << "Zip::SetArchive() connect to archive '" << uri << "', password: " << (pass ? pass : "(empty)") << ".\n"; password = pass; return uri ? (zfile = unzOpen2(uri, &Zip_filefunc)) != NULL : false; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ uint Zip::GetCaps() const { return CanRead | CanSeek| IsCaseSensitive; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ Handle *Zip::Open(const char *path, Mode mode) { if (!zfile) __ERR__(__LOG_E__ << "Cannot open file with no archive support.\n", NULL) if (mode == ModeWrite) return NULL; // unsupported /* Query the memory file system so that multiple accesses to the same compressed file will share a single uncompressed memory buffer. */ bool is_memory_handle = false; Handle *h = memfs->Open(path, mode); if (!h) if (unzLocateFile(zfile, path, 1) == UNZ_OK) // case-sensitive { // Get file info. unz_file_info info; unzGetCurrentFileInfo(zfile, &info, 0, 0, 0, 0, 0, 0); if (info.compression_method == 0) { unzOpenCurrentFile(zfile); size_t offset = (size_t)unzGetCurrentFileZStreamPos64(zfile); unzCloseCurrentFile(zfile); __LOG__ << "Mapping zip segment to '" << path << "' @" << int(offset) << "\n"; Handle *zh = (Handle *)unzGetFileStream(zfile); h = new HandleSegment(zh, offset, info.uncompressed_size); } else { __LOG__ << "Mapping unzipped '" << path << "' to memory...\n"; // Load the whole file into memory. Array data(info.uncompressed_size, Alloc::Filesystem); if (data.GetSize() != info.uncompressed_size) __ERR__(__LOG_E__ << "Failed to allocate decompression space for '" << path << "'.\n", NULL) int r = password.IsEmpty() ? unzOpenCurrentFile(zfile) : unzOpenCurrentFilePassword(zfile, password); if (r != UNZ_OK) __ERR__(__LOG_E__ << "Failed to open file '" << path << "'.\n", NULL) if (unzReadCurrentFile(zfile, (voidp)data.c_ptr(), info.uncompressed_size) != (int)info.uncompressed_size) __ERR__(__LOG_E__ << "Failed to read file '" << path << "'.\n", NULL) unzCloseCurrentFile(zfile); // Write to the support I/O memory filesystem. AutoPtr wh(memfs->Open(path, ModeWrite)); if (wh.IsValid()) wh->Write(data.c_ptr(), data.GetSize()); // Open memory based uncompressed file. h = memfs->Open(path, mode); is_memory_handle = true; } } if (h) if (ZipHandle *z = new ZipHandle(this)) { // Wrap memory I/O handle. if (is_memory_handle) { // Increase refcount for this file. Pair *p = refc_map.Get(path); if (!p) p = refc_map.Add(path, 0); ++p->value; z->p = p; } z->h = h; return z; } return NULL; } void Zip::Close(Handle *h) { if (ZipHandle *z = (ZipHandle *)h) { if (z->p) { // If refcount for this entry reaches 0, drop it from the support I/O. if (--z->p->value == 0) { memfs->Delete(z->p->key); refc_map.Delete(z->p); } z->p = NULL; } z->h = NULL; } } bool Zip::Delete(const char *uri) { return false; } size_t Zip::Seek(Handle *h, ptrdiff_t offset, SeekRef ref) { if (ZipHandle *z = (ZipHandle *)h) return z->h->Seek(offset, ref); return (size_t)-1; } size_t Zip::Tell(Handle *h) { if (ZipHandle *z = (ZipHandle *)h) return z->h->Tell(); return (size_t)-1; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ size_t Zip::Read(Handle *h, void *b, size_t size) { if (ZipHandle *z = (ZipHandle *)h) return z->h->Read(b, size); return 0; } size_t Zip::Write(Handle *h, const void *b, size_t size) { return 0; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ Zip::Zip(const char *uri, const char *password) { zfile = NULL; memfs = new Memory; SetArchive(uri, password); } Zip::~Zip() { SetArchive(NULL, NULL); } //------------------------------------------------------------------------------