/* Copyright (C) 2001-2006, William Joseph. All Rights Reserved. This file is part of GtkRadiant. GtkRadiant is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. GtkRadiant is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "idatastream.h" #include "cmdlib.h" #include "bytestreamutils.h" #include "modulesystem.h" #include "iarchive.h" #include #include "stream/filestream.h" #include "container/array.h" #include "archivelib.h" #include "zlibstream.h" class DeflatedArchiveFile : public ArchiveFile { CopiedString m_name; FileInputStream m_istream; SubFileInputStream m_substream; DeflatedInputStream m_zipstream; FileInputStream::size_type m_size; public: typedef FileInputStream::size_type size_type; typedef FileInputStream::position_type position_type; DeflatedArchiveFile( const char* name, const char* archiveName, position_type position, size_type stream_size, size_type file_size ) : m_name( name ), m_istream( archiveName ), m_substream( m_istream, position, stream_size ), m_zipstream( m_substream ), m_size( file_size ){ } void release(){ delete this; } size_type size() const { return m_size; } const char* getName() const { return m_name.c_str(); } InputStream& getInputStream(){ return m_zipstream; } }; class DeflatedArchiveTextFile : public ArchiveTextFile { CopiedString m_name; FileInputStream m_istream; SubFileInputStream m_substream; DeflatedInputStream m_zipstream; BinaryToTextInputStream m_textStream; public: typedef FileInputStream::size_type size_type; typedef FileInputStream::position_type position_type; DeflatedArchiveTextFile( const char* name, const char* archiveName, position_type position, size_type stream_size ) : m_name( name ), m_istream( archiveName ), m_substream( m_istream, position, stream_size ), m_zipstream( m_substream ), m_textStream( m_zipstream ){ } void release(){ delete this; } TextInputStream& getInputStream(){ return m_textStream; } }; #include "pkzip.h" #include #include "string/string.h" #include "fs_filesystem.h" class ZipArchive : public Archive { class ZipRecord { public: enum ECompressionMode { eStored, eDeflated, }; ZipRecord( unsigned int position, unsigned int compressed_size, unsigned int uncompressed_size, ECompressionMode mode ) : m_position( position ), m_stream_size( compressed_size ), m_file_size( uncompressed_size ), m_mode( mode ){ } unsigned int m_position; unsigned int m_stream_size; unsigned int m_file_size; ECompressionMode m_mode; }; typedef GenericFileSystem ZipFileSystem; ZipFileSystem m_filesystem; CopiedString m_name; FileInputStream m_istream; bool read_record(){ zip_magic magic; istream_read_zip_magic( m_istream, magic ); if ( !( magic == zip_root_dirent_magic ) ) { return false; } zip_version version_encoder; istream_read_zip_version( m_istream, version_encoder ); zip_version version_extract; istream_read_zip_version( m_istream, version_extract ); //unsigned short flags = istream_read_int16_le( m_istream ); unsigned short compression_mode = istream_read_int16_le( m_istream ); if ( compression_mode != Z_DEFLATED && compression_mode != 0 ) { return false; } zip_dostime dostime; istream_read_zip_dostime( m_istream, dostime ); //unsigned int crc32 = istream_read_int32_le( m_istream ); unsigned int compressed_size = istream_read_uint32_le( m_istream ); unsigned int uncompressed_size = istream_read_uint32_le( m_istream ); unsigned int namelength = istream_read_uint16_le( m_istream ); unsigned short extras = istream_read_uint16_le( m_istream ); unsigned short comment = istream_read_uint16_le( m_istream ); //unsigned short diskstart = istream_read_int16_le( m_istream ); //unsigned short filetype = istream_read_int16_le( m_istream ); //unsigned int filemode = istream_read_int32_le( m_istream ); unsigned int position = istream_read_int32_le( m_istream ); Array filename( namelength + 1 ); m_istream.read( reinterpret_cast( filename.data() ), namelength ); filename[namelength] = '\0'; m_istream.seek( extras + comment, FileInputStream::cur ); if ( path_is_directory( filename.data() ) ) { m_filesystem[filename.data()] = 0; } else { ZipFileSystem::entry_type& file = m_filesystem[filename.data()]; if ( !file.is_directory() ) { globalOutputStream() << "Warning: zip archive " << makeQuoted( m_name.c_str() ) << " contains duplicated file: " << makeQuoted( filename.data() ) << "\n"; } else { file = new ZipRecord( position, compressed_size, uncompressed_size, ( compression_mode == Z_DEFLATED ) ? ZipRecord::eDeflated : ZipRecord::eStored ); } } return true; } bool read_pkzip(){ SeekableStream::position_type pos = pkzip_find_disk_trailer( m_istream ); if ( pos != 0 ) { zip_disk_trailer disk_trailer; m_istream.seek( pos ); istream_read_zip_disk_trailer( m_istream, disk_trailer ); if ( !( disk_trailer.z_magic == zip_disk_trailer_magic ) ) { return false; } m_istream.seek( disk_trailer.z_rootseek ); for ( unsigned int i = 0; i < disk_trailer.z_entries; ++i ) { if ( !read_record() ) { return false; } } return true; } return false; } public: ZipArchive( const char* name ) : m_name( name ), m_istream( name ){ if ( !m_istream.failed() ) { if ( !read_pkzip() ) { globalErrorStream() << "ERROR: invalid zip-file " << makeQuoted( name ) << '\n'; } } } ~ZipArchive(){ for ( ZipFileSystem::iterator i = m_filesystem.begin(); i != m_filesystem.end(); ++i ) { delete i->second.file(); } } bool failed(){ return m_istream.failed(); } void release(){ delete this; } ArchiveFile* openFile( const char* name ){ ZipFileSystem::iterator i = m_filesystem.find( name ); if ( i != m_filesystem.end() && !i->second.is_directory() ) { ZipRecord* file = i->second.file(); m_istream.seek( file->m_position ); zip_file_header file_header; istream_read_zip_file_header( m_istream, file_header ); if ( file_header.z_magic != zip_file_header_magic ) { globalErrorStream() << "error reading zip file " << makeQuoted( m_name.c_str() ); return 0; } switch ( file->m_mode ) { case ZipRecord::eStored: return StoredArchiveFile::create( name, m_name.c_str(), m_istream.tell(), file->m_stream_size, file->m_file_size ); case ZipRecord::eDeflated: return new DeflatedArchiveFile( name, m_name.c_str(), m_istream.tell(), file->m_stream_size, file->m_file_size ); } } return 0; } ArchiveTextFile* openTextFile( const char* name ){ ZipFileSystem::iterator i = m_filesystem.find( name ); if ( i != m_filesystem.end() && !i->second.is_directory() ) { ZipRecord* file = i->second.file(); m_istream.seek( file->m_position ); zip_file_header file_header; istream_read_zip_file_header( m_istream, file_header ); if ( file_header.z_magic != zip_file_header_magic ) { globalErrorStream() << "error reading zip file " << makeQuoted( m_name.c_str() ); return 0; } switch ( file->m_mode ) { case ZipRecord::eStored: return StoredArchiveTextFile::create( name, m_name.c_str(), m_istream.tell(), file->m_stream_size ); case ZipRecord::eDeflated: return new DeflatedArchiveTextFile( name, m_name.c_str(), m_istream.tell(), file->m_stream_size ); } } return 0; } bool containsFile( const char* name ){ ZipFileSystem::iterator i = m_filesystem.find( name ); return i != m_filesystem.end() && !i->second.is_directory(); } void forEachFile( VisitorFunc visitor, const char* root ){ m_filesystem.traverse( visitor, root ); } }; Archive* OpenArchive( const char* name ){ return new ZipArchive( name ); } #if 0 class TestZip { class TestVisitor : public Archive::IVisitor { public: void visit( const char* name ){ int bleh = 0; } }; public: TestZip(){ testzip( "c:/quake3/baseq3/mapmedia.pk3", "textures/radiant/notex.tga" ); } void testzip( const char* name, const char* filename ){ Archive* archive = OpenArchive( name ); ArchiveFile* file = archive->openFile( filename ); if ( file != 0 ) { unsigned char buffer[4096]; std::size_t count = file->getInputStream().read( (InputStream::byte_type*)buffer, 4096 ); file->release(); } TestVisitor visitor; archive->forEachFile( Archive::VisitorFunc( &visitor, Archive::eFilesAndDirectories, 0 ), "" ); archive->forEachFile( Archive::VisitorFunc( &visitor, Archive::eFilesAndDirectories, 1 ), "" ); archive->forEachFile( Archive::VisitorFunc( &visitor, Archive::eFiles, 1 ), "" ); archive->forEachFile( Archive::VisitorFunc( &visitor, Archive::eDirectories, 1 ), "" ); archive->forEachFile( Archive::VisitorFunc( &visitor, Archive::eFilesAndDirectories, 1 ), "textures" ); archive->forEachFile( Archive::VisitorFunc( &visitor, Archive::eFilesAndDirectories, 1 ), "textures/" ); archive->forEachFile( Archive::VisitorFunc( &visitor, Archive::eFilesAndDirectories, 2 ), "" ); archive->release(); } }; TestZip g_TestZip; #endif