1 /*
2  * The Doomsday Engine Project -- libcore
3  *
4  * Copyright © 2004-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
5  *
6  * @par License
7  * LGPL: http://www.gnu.org/licenses/lgpl.html
8  *
9  * <small>This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 3 of the License, or (at your
12  * option) any later version. This program is distributed in the hope that it
13  * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
15  * General Public License for more details. You should have received a copy of
16  * the GNU Lesser General Public License along with this program; if not, see:
17  * http://www.gnu.org/licenses</small>
18  */
19 
20 #ifndef LIBDENG2_ZIPARCHIVE_H
21 #define LIBDENG2_ZIPARCHIVE_H
22 
23 #include "../Archive"
24 #include "../NativePath"
25 #include "../filesys/IInterpreter"
26 
27 namespace de {
28 
29 /**
30  * Archive whose serialization uses the ZIP file format.
31  * @ingroup data
32  *
33  * All the features of the ZIP format are not supported:
34  * - Deflate is the only supported compression method.
35  * - Multipart ZIP files are not supported.
36  *
37  * @see http://en.wikipedia.org/wiki/Zip_(file_format)
38  */
39 class DENG2_PUBLIC ZipArchive : public Archive
40 {
41 public:
42     /// The central directory of the ZIP archive cannot be located. Maybe it's not
43     /// a ZIP archive after all? @ingroup errors
44     DENG2_SUB_ERROR(FormatError, MissingCentralDirectoryError);
45 
46     /// The source archive belongs to a multipart archive. @ingroup errors
47     DENG2_SUB_ERROR(FormatError, MultiPartError);
48 
49     /// An entry in the archive uses a compression algorithm not supported by the
50     /// implementation. @ingroup errors
51     DENG2_SUB_ERROR(FormatError, UnknownCompressionError);
52 
53     /// An entry is encrypted. Decrypting is not supported. @ingroup errors
54     DENG2_SUB_ERROR(FormatError, EncryptionError);
55 
56     /// There is an error during decompression. @ingroup errors
57     DENG2_SUB_ERROR(ContentError, InflateError);
58 
59     /// There is an error during compression. @ingroup errors
60     DENG2_SUB_ERROR(ContentError, DeflateError);
61 
62 public:
63     /**
64      * Constructs an empty ZIP archive.
65      */
66     ZipArchive();
67 
68     /**
69      * Constructs a new ZIP archive instance. The content index contained
70      * in @a data is read during construction.
71      *
72      * @param data  Data of the source archive. No copy of the
73      *              data is made, so the caller must make sure the
74      *              byte array remains in existence for the lifetime
75      *              of the Archive instance.
76      * @param dirCacheId  ID of cached ZIP directory data.
77      */
78     ZipArchive(IByteArray const &data, Block const &dirCacheId = Block());
79 
80     void operator >> (Writer &to) const;
81 
82 public:
83     /**
84      * Determines whether a File looks like it could be accessed using ZipArchive.
85      *
86      * @param file  File to check.
87      *
88      * @return @c true, if the file looks like an archive.
89      */
90     static bool recognize(File const &file);
91 
92     /**
93      * Determines whether a native file looks like it could be in ZIP format.
94      *
95      * @param path  Native path of the file to check.
96      *
97      * @return @c true, if the file looks like an archive.
98      */
99     static bool recognize(NativePath const &path);
100 
101     struct DENG2_PUBLIC Interpreter : public filesys::IInterpreter {
102         File *interpretFile(File *sourceData) const override;
103     };
104 
105 protected:
106     void readFromSource(Entry const &entry, Path const &path, IBlock &uncompressedData) const;
107 
108     struct ZipEntry : public Entry
109     {
110         duint16 compression;        ///< Type of compression employed by the entry.
111         duint32 crc32;              ///< CRC32 checksum.
112         dsize localHeaderOffset;    ///< Offset of the local file header.
113 
ZipEntryZipEntry114         ZipEntry(PathTree::NodeArgs const &args) : Entry(args),
115               compression(0), crc32(0), localHeaderOffset(0) {}
116 
117         /// Recalculates CRC32 of the entry.
118         void update();
119     };
120 
121     typedef PathTreeT<ZipEntry> Index;
122 
123     Index const &index() const;
124 
125 private:
126     DENG2_PRIVATE(d)
127 };
128 
129 } // namespace de
130 
131 #endif // LIBDENG2_ZIPARCHIVE_H
132