2 Copyright (C) 2001-2006, William Joseph.
5 This file is part of GtkRadiant.
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 #include "idatastream.h"
24 #include "bytestreamutils.h"
26 #include "modulesystem.h"
30 #include "stream/filestream.h"
31 #include "container/array.h"
32 #include "archivelib.h"
33 #include "zlibstream.h"
35 class DeflatedArchiveFile : public ArchiveFile
38 FileInputStream m_istream;
39 SubFileInputStream m_substream;
40 DeflatedInputStream m_zipstream;
41 FileInputStream::size_type m_size;
43 typedef FileInputStream::size_type size_type;
44 typedef FileInputStream::position_type position_type;
46 DeflatedArchiveFile( const char* name, const char* archiveName, position_type position, size_type stream_size, size_type file_size )
47 : m_name( name ), m_istream( archiveName ), m_substream( m_istream, position, stream_size ), m_zipstream( m_substream ), m_size( file_size ){
53 size_type size() const {
56 const char* getName() const {
57 return m_name.c_str();
59 InputStream& getInputStream(){
64 class DeflatedArchiveTextFile : public ArchiveTextFile
67 FileInputStream m_istream;
68 SubFileInputStream m_substream;
69 DeflatedInputStream m_zipstream;
70 BinaryToTextInputStream<DeflatedInputStream> m_textStream;
72 typedef FileInputStream::size_type size_type;
73 typedef FileInputStream::position_type position_type;
75 DeflatedArchiveTextFile( const char* name, const char* archiveName, position_type position, size_type stream_size )
76 : m_name( name ), m_istream( archiveName ), m_substream( m_istream, position, stream_size ), m_zipstream( m_substream ), m_textStream( m_zipstream ){
82 TextInputStream& getInputStream(){
90 #include "string/string.h"
91 #include "fs_filesystem.h"
94 class ZipArchive : public Archive
104 ZipRecord( unsigned int position, unsigned int compressed_size, unsigned int uncompressed_size, ECompressionMode mode )
105 : m_position( position ), m_stream_size( compressed_size ), m_file_size( uncompressed_size ), m_mode( mode ){
107 unsigned int m_position;
108 unsigned int m_stream_size;
109 unsigned int m_file_size;
110 ECompressionMode m_mode;
113 typedef GenericFileSystem<ZipRecord> ZipFileSystem;
114 ZipFileSystem m_filesystem;
116 FileInputStream m_istream;
120 istream_read_zip_magic( m_istream, magic );
121 if ( !( magic == zip_root_dirent_magic ) ) {
124 zip_version version_encoder;
125 istream_read_zip_version( m_istream, version_encoder );
126 zip_version version_extract;
127 istream_read_zip_version( m_istream, version_extract );
128 //unsigned short flags =
129 istream_read_int16_le( m_istream );
130 unsigned short compression_mode = istream_read_int16_le( m_istream );
131 if ( compression_mode != Z_DEFLATED && compression_mode != 0 ) {
135 istream_read_zip_dostime( m_istream, dostime );
136 //unsigned int crc32 =
137 istream_read_int32_le( m_istream );
138 unsigned int compressed_size = istream_read_uint32_le( m_istream );
139 unsigned int uncompressed_size = istream_read_uint32_le( m_istream );
140 unsigned int namelength = istream_read_uint16_le( m_istream );
141 unsigned short extras = istream_read_uint16_le( m_istream );
142 unsigned short comment = istream_read_uint16_le( m_istream );
143 //unsigned short diskstart =
144 istream_read_int16_le( m_istream );
145 //unsigned short filetype =
146 istream_read_int16_le( m_istream );
147 //unsigned int filemode =
148 istream_read_int32_le( m_istream );
149 unsigned int position = istream_read_int32_le( m_istream );
151 std::string filename( namelength, ' ' );
152 if ( namelength > 0 )
153 m_istream.read( reinterpret_cast<FileInputStream::byte_type*>( &filename[0] ), namelength );
155 m_istream.seek( extras + comment, FileInputStream::cur );
157 if ( path_is_directory( filename.c_str() ) ) {
158 m_filesystem[filename.c_str()] = 0;
162 ZipFileSystem::entry_type& file = m_filesystem[filename.c_str()];
163 if ( !file.is_directory() ) {
164 globalOutputStream() << "Warning: zip archive " << makeQuoted( m_name.c_str() ) << " contains duplicated file: " << makeQuoted( filename.c_str() ) << "\n";
168 file = new ZipRecord( position, compressed_size, uncompressed_size, ( compression_mode == Z_DEFLATED ) ? ZipRecord::eDeflated : ZipRecord::eStored );
176 SeekableStream::position_type pos = pkzip_find_disk_trailer( m_istream );
178 zip_disk_trailer disk_trailer;
179 m_istream.seek( pos );
180 istream_read_zip_disk_trailer( m_istream, disk_trailer );
181 if ( !( disk_trailer.z_magic == zip_disk_trailer_magic ) ) {
185 m_istream.seek( disk_trailer.z_rootseek );
186 for ( unsigned int i = 0; i < disk_trailer.z_entries; ++i )
188 if ( !read_record() ) {
197 ZipArchive( const char* name )
198 : m_name( name ), m_istream( name ){
199 if ( !m_istream.failed() ) {
200 if ( !read_pkzip() ) {
201 globalErrorStream() << "ERROR: invalid zip-file " << makeQuoted( name ) << '\n';
206 for ( ZipFileSystem::iterator i = m_filesystem.begin(); i != m_filesystem.end(); ++i )
208 delete i->second.file();
213 return m_istream.failed();
219 ArchiveFile* openFile( const char* name ){
220 ZipFileSystem::iterator i = m_filesystem.find( name );
221 if ( i != m_filesystem.end() && !i->second.is_directory() ) {
222 ZipRecord* file = i->second.file();
224 m_istream.seek( file->m_position );
225 zip_file_header file_header;
226 istream_read_zip_file_header( m_istream, file_header );
227 if ( file_header.z_magic != zip_file_header_magic ) {
228 globalErrorStream() << "error reading zip file " << makeQuoted( m_name.c_str() );
232 switch ( file->m_mode )
234 case ZipRecord::eStored:
235 return StoredArchiveFile::create( name, m_name.c_str(), m_istream.tell(), file->m_stream_size, file->m_file_size );
236 case ZipRecord::eDeflated:
237 return new DeflatedArchiveFile( name, m_name.c_str(), m_istream.tell(), file->m_stream_size, file->m_file_size );
242 ArchiveTextFile* openTextFile( const char* name ){
243 ZipFileSystem::iterator i = m_filesystem.find( name );
244 if ( i != m_filesystem.end() && !i->second.is_directory() ) {
245 ZipRecord* file = i->second.file();
247 m_istream.seek( file->m_position );
248 zip_file_header file_header;
249 istream_read_zip_file_header( m_istream, file_header );
250 if ( file_header.z_magic != zip_file_header_magic ) {
251 globalErrorStream() << "error reading zip file " << makeQuoted( m_name.c_str() );
255 switch ( file->m_mode )
257 case ZipRecord::eStored:
258 return StoredArchiveTextFile::create( name, m_name.c_str(), m_istream.tell(), file->m_stream_size );
259 case ZipRecord::eDeflated:
260 return new DeflatedArchiveTextFile( name, m_name.c_str(), m_istream.tell(), file->m_stream_size );
265 bool containsFile( const char* name ){
266 ZipFileSystem::iterator i = m_filesystem.find( name );
267 return i != m_filesystem.end() && !i->second.is_directory();
269 void forEachFile( VisitorFunc visitor, const char* root ){
270 m_filesystem.traverse( visitor, root );
274 Archive* OpenArchive( const char* name ){
275 return new ZipArchive( name );
282 class TestVisitor : public Archive::IVisitor
285 void visit( const char* name ){
291 testzip( "c:/quake3/baseq3/mapmedia.pk3", "textures/radiant/notex.tga" );
294 void testzip( const char* name, const char* filename ){
295 Archive* archive = OpenArchive( name );
296 ArchiveFile* file = archive->openFile( filename );
298 unsigned char buffer[4096];
299 std::size_t count = file->getInputStream().read( (InputStream::byte_type*)buffer, 4096 );
303 archive->forEachFile( Archive::VisitorFunc( &visitor, Archive::eFilesAndDirectories, 0 ), "" );
304 archive->forEachFile( Archive::VisitorFunc( &visitor, Archive::eFilesAndDirectories, 1 ), "" );
305 archive->forEachFile( Archive::VisitorFunc( &visitor, Archive::eFiles, 1 ), "" );
306 archive->forEachFile( Archive::VisitorFunc( &visitor, Archive::eDirectories, 1 ), "" );
307 archive->forEachFile( Archive::VisitorFunc( &visitor, Archive::eFilesAndDirectories, 1 ), "textures" );
308 archive->forEachFile( Archive::VisitorFunc( &visitor, Archive::eFilesAndDirectories, 1 ), "textures/" );
309 archive->forEachFile( Archive::VisitorFunc( &visitor, Archive::eFilesAndDirectories, 2 ), "" );