xoreos  0.0.5
zipfile.cpp
Go to the documentation of this file.
1 /* xoreos - A reimplementation of BioWare's Aurora engine
2  *
3  * xoreos is the legal property of its developers, whose names
4  * can be found in the AUTHORS file distributed with this source
5  * distribution.
6  *
7  * xoreos is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 3
10  * of the License, or (at your option) any later version.
11  *
12  * xoreos 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.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with xoreos. If not, see <http://www.gnu.org/licenses/>.
19  */
20 
25 #include <cassert>
26 
27 #include "src/common/zipfile.h"
28 #include "src/common/error.h"
29 #include "src/common/util.h"
30 #include "src/common/strutil.h"
31 #include "src/common/encoding.h"
33 #include "src/common/deflate.h"
34 
35 namespace Common {
36 
38  assert(_zip);
39 
40  load(*_zip);
41 }
42 
44 }
45 
47  static const byte kEndRecord[4] = { 0x50, 0x4B, 0x05, 0x06 };
48 
49  const size_t endPos = searchBackwards(zip, kEndRecord, sizeof(kEndRecord), 0xFFFF);
50  if (endPos == SIZE_MAX)
51  throw Exception("End of central directory record not found");
52 
53  zip.seek(endPos);
54 
55  zip.skip(4); // Header, already checked
56 
57  uint16 curDisk = zip.readUint16LE();
58  uint16 centralDirDisk = zip.readUint16LE();
59 
60  uint16 curDiskDirs = zip.readUint16LE();
61  uint16 totalDirs = zip.readUint16LE();
62 
63  if ((curDisk != 0) || (curDisk != centralDirDisk) || (curDiskDirs != totalDirs))
64  throw Exception("Unsupported multi-disk ZIP file");
65 
66  zip.skip(4); // Size of central directory
67 
68  uint32 centralDirPos = zip.readUint32LE();
69  zip.seek(centralDirPos);
70 
71  uint32 tag = zip.readUint32LE();
72  if (tag != 0x02014B50)
73  throw Exception("Unknown ZIP record %08X", tag);
74 
75  _iFiles.reserve(totalDirs);
76  while (tag == 0x02014B50) {
77  File file;
78  IFile iFile;
79 
80  zip.skip(20);
81 
82  iFile.size = zip.readUint32LE();
83 
84  uint16 nameLength = zip.readUint16LE();
85  uint16 extraLength = zip.readUint16LE();
86  uint16 commentLength = zip.readUint16LE();
87  uint16 diskNum = zip.readUint16LE();
88 
89  if (diskNum != 0)
90  throw Exception("Unsupported multi-disk ZIP file");
91 
92  zip.skip(6); // File attributes
93 
94  iFile.offset = zip.readUint32LE();
95 
96  file.name = readStringFixed(zip, kEncodingASCII, nameLength).toLower();
97 
98  zip.skip(extraLength);
99  zip.skip(commentLength);
100 
101  tag = zip.readUint32LE();
102  if ((tag != 0x02014B50) && (tag != 0x06054B50))
103  throw Exception("Unknown ZIP record %08X", tag);
104 
105  // Ignore empty file names
106  if (!file.name.empty()) {
107  // HACK: Skip any filename with a trailing slash because it's
108  // a directory. The proper solution would be to interpret the
109  // file attributes.
110 
111  if (*(--file.name.end()) != '/') {
112  file.index = _iFiles.size();
113 
114  _files.push_back(file);
115  _iFiles.push_back(iFile);
116  }
117  }
118  }
119 }
120 
122  return _files;
123 }
124 
126  if (index >= _iFiles.size())
127  throw Exception("File index out of range (%u/%u)", index, (uint)_iFiles.size());
128 
129  return _iFiles[index];
130 }
131 
133  uint16 &compMethod, uint32 &compSize, uint32 &realSize) const {
134 
135  zip.seek(file.offset);
136 
137  uint32 tag = zip.readUint32LE();
138  if (tag != 0x04034B50)
139  throw Exception("Unknown ZIP record %08X", tag);
140 
141  zip.skip(4);
142 
143  compMethod = zip.readUint16LE();
144 
145  zip.skip(8);
146 
147  compSize = zip.readUint32LE();
148  realSize = zip.readUint32LE();
149 
150  uint16 nameLength = zip.readUint16LE();
151  uint16 extraLength = zip.readUint16LE();
152 
153  zip.skip(nameLength);
154  zip.skip(extraLength);
155 }
156 
157 size_t ZipFile::getFileSize(uint32 index) const {
158  return getIFile(index).size;
159 }
160 
161 SeekableReadStream *ZipFile::getFile(uint32 index, bool tryNoCopy) const {
162  const IFile &file = getIFile(index);
163 
164  uint16 compMethod;
165  uint32 compSize;
166  uint32 realSize;
167 
168  getFileProperties(*_zip, file, compMethod, compSize, realSize);
169 
170  if (tryNoCopy && (compMethod == 0))
171  return new SeekableSubReadStream(_zip.get(), _zip->pos(), _zip->pos() + compSize);
172 
173  return decompressFile(*_zip, compMethod, compSize, realSize);
174 }
175 
177  uint32 compSize, uint32 realSize) {
178 
179  if (method == 0) {
180  // Uncompressed
181 
182  return zip.readStream(compSize);
183  }
184 
185  if (method != 8)
186  throw Exception("Unhandled Zip compression %d", method);
187 
188  return decompressDeflate(zip, compSize, realSize, kWindowBitsMaxRaw);
189 }
190 
191 } // End of namespace Common
UString name
The file&#39;s name.
Definition: zipfile.h:46
uint16 readUint16LE()
Read an unsigned 16-bit word stored in little endian (LSB first) order from the stream and return it...
Definition: readstream.h:122
Internal file information.
Definition: zipfile.h:66
uint32 readUint32LE()
Read an unsigned 32-bit word stored in little endian (LSB first) order from the stream and return it...
Definition: readstream.h:133
Definition: 2dafile.h:39
virtual size_t seek(ptrdiff_t offset, Origin whence=kOriginBegin)=0
Sets the stream position indicator for the stream.
MemoryReadStream * readStream(size_t dataSize)
Read the specified amount of data into a new[]&#39;ed buffer which then is wrapped into a MemoryReadStrea...
Definition: readstream.cpp:67
Implementing the reading stream interfaces for plain memory blocks.
ScopedPtr< SeekableReadStream > _zip
Definition: zipfile.h:73
ZIP file decompression.
byte * decompressDeflate(const byte *data, size_t inputSize, size_t outputSize, int windowBits)
Decompress (inflate) using zlib&#39;s DEFLATE algorithm.
Definition: deflate.cpp:63
Utility templates and functions for working with strings and streams.
SeekableReadStream * getFile(uint32 index, bool tryNoCopy=false) const
Return a stream of the file&#39;s contents.
Definition: zipfile.cpp:161
Basic exceptions to throw.
static SeekableReadStream * decompressFile(SeekableReadStream &zip, uint32 method, uint32 compSize, uint32 realSize)
Definition: zipfile.cpp:176
size_t searchBackwards(SeekableReadStream &haystack, const byte *needle, size_t needleSize, size_t maxReadBack)
Search the stream, backwards, for the last occurrence of a set of bytes.
Definition: strutil.cpp:349
const FileList & getFiles() const
Return the list of files.
Definition: zipfile.cpp:121
ZipFile(SeekableReadStream *zip)
Definition: zipfile.cpp:37
static const int kWindowBitsMaxRaw
Definition: deflate.h:42
uint16_t uint16
Definition: types.h:202
Utility templates and functions.
std::list< File > FileList
Definition: zipfile.h:50
virtual size_t skip(ptrdiff_t offset)
Skip the specified number of bytes, adding that offset to the current position in the stream...
Definition: readstream.h:317
uint32 size
The file&#39;s size.
Definition: zipfile.h:68
Utility functions for working with differing string encodings.
bool empty() const
Is the string empty?
Definition: ustring.cpp:245
StackException Exception
Definition: error.h:59
size_t getFileSize(uint32 index) const
Return the size of a file.
Definition: zipfile.cpp:157
void load(SeekableReadStream &zip)
Definition: zipfile.cpp:46
Plain, unextended ASCII (7bit clean).
Definition: encoding.h:40
UString toLower() const
Return a lowercased copy of the string.
Definition: ustring.cpp:481
uint32_t uint32
Definition: types.h:204
FileList _files
External list of file names and types.
Definition: zipfile.h:76
#define SIZE_MAX
Definition: types.h:172
IFileList _iFiles
Internal list of file offsets and sizes.
Definition: zipfile.h:79
const IFile & getIFile(uint32 index) const
Definition: zipfile.cpp:125
UString readStringFixed(SeekableReadStream &stream, Encoding encoding, size_t length)
Read length bytes as a string with the given encoding out of a stream.
Definition: encoding.cpp:297
uint32 offset
The offset of the file within the ZIP.
Definition: zipfile.h:67
void getFileProperties(SeekableReadStream &zip, const IFile &file, uint16 &compMethod, uint32 &compSize, uint32 &realSize) const
Definition: zipfile.cpp:132
iterator end() const
Definition: ustring.cpp:257
SeekableSubReadStream provides access to a SeekableReadStream restricted to the range [begin...
Definition: readstream.h:359
Compress (deflate) and decompress (inflate) using zlib&#39;s DEFLATE algorithm.
Interface for a seekable & readable data stream.
Definition: readstream.h:265
uint8 byte
Definition: types.h:209
uint32 index
The file&#39;s local index within the ZIP.
Definition: zipfile.h:47
unsigned int uint
Definition: types.h:211