Merge lp:~widelands-dev/widelands/fix_zip_filesystem into lp:widelands
- fix_zip_filesystem
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 7711 |
Proposed branch: | lp:~widelands-dev/widelands/fix_zip_filesystem |
Merge into: | lp:widelands |
Diff against target: |
819 lines (+277/-283) 2 files modified
src/io/filesystem/zip_filesystem.cc (+209/-253) src/io/filesystem/zip_filesystem.h (+68/-30) |
To merge this branch: | bzr merge lp:~widelands-dev/widelands/fix_zip_filesystem |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
TiborB | Approve | ||
Review via email: mp+282884@code.launchpad.net |
Commit message
Use std::shared_ptr to properly share zip files between classes.
ZipFilesystem, any child filesystems created by make_sub_
This changes the code to move the zip file abstraction into a wrapper class that properly reopens files for the correct access.
This also get's rid of the improper (and deprecated) use of the implicit copy constructor - the state was copied, but not properly shared before.
Description of the change
Fix deprecated use of implicit copy constructor and the mess that ZipFilesystem was in general.
SirVer (sirver) wrote : | # |
bunnybot (widelandsofficial) wrote : | # |
Hi, I am bunnybot (https:/
I am keeping the source branch lp:~widelands-dev/widelands/fix_zip_filesystem mirrored to https:/
You can give me commands by starting a line with @bunnybot <command>. I understand:
merge: Merges the source branch into the target branch, closing the merge proposal. I will use the proposed commit message if it is set.
bunnybot (widelandsofficial) wrote : | # |
Travis build 295 has changed state to: passed. Details: https:/
TiborB (tiborb95) wrote : | # |
Tested - briefly - no issues
code looks good to me as far as I can tell
SirVer (sirver) wrote : | # |
Thanks!
@bunnybot merge
Preview Diff
1 | === modified file 'src/io/filesystem/zip_filesystem.cc' |
2 | --- src/io/filesystem/zip_filesystem.cc 2015-10-25 15:44:36 +0000 |
3 | +++ src/io/filesystem/zip_filesystem.cc 2016-01-17 21:52:36 +0000 |
4 | @@ -35,28 +35,99 @@ |
5 | #include "io/streamread.h" |
6 | #include "io/streamwrite.h" |
7 | |
8 | +ZipFilesystem::ZipFile::ZipFile(const std::string& zipfile) |
9 | + : state_(State::kIdle), |
10 | + path_(zipfile), |
11 | + basename_(fs_filename(zipfile.c_str())), |
12 | + write_handle_(nullptr), |
13 | + read_handle_(nullptr) { |
14 | +} |
15 | + |
16 | +ZipFilesystem::ZipFile::~ZipFile() { |
17 | + close(); |
18 | +} |
19 | + |
20 | +void ZipFilesystem::ZipFile::close() { |
21 | + if (state_ == State::kZipping) { |
22 | + zipClose(write_handle_, nullptr); |
23 | + } else if (state_ == State::kUnzipping) { |
24 | + unzClose(read_handle_); |
25 | + } |
26 | + state_ = State::kIdle; |
27 | +} |
28 | + |
29 | +void ZipFilesystem::ZipFile::open_for_zip() { |
30 | + if (state_ == State::kZipping) |
31 | + return; |
32 | + |
33 | + close(); |
34 | + |
35 | + write_handle_ = zipOpen(path_.c_str(), APPEND_STATUS_ADDINZIP); |
36 | + if (!write_handle_) { |
37 | + // Couldn't open for append, so create new. |
38 | + write_handle_ = zipOpen(path_.c_str(), APPEND_STATUS_CREATE); |
39 | + } |
40 | + |
41 | + state_ = State::kZipping; |
42 | +} |
43 | + |
44 | +void ZipFilesystem::ZipFile::open_for_unzip() { |
45 | + if (state_ == State::kUnzipping) |
46 | + return; |
47 | + |
48 | + close(); |
49 | + |
50 | + read_handle_ = unzOpen(path_.c_str()); |
51 | + if (!read_handle_) |
52 | + throw FileTypeError |
53 | + ("ZipFilesystem::open_for_unzip", path_, "not a .zip file"); |
54 | + state_ = State::kUnzipping; |
55 | +} |
56 | + |
57 | +std::string ZipFilesystem::ZipFile::strip_basename(const std::string& filename) { |
58 | + if (filename.compare(0, basename_.length(), basename_) == 0) |
59 | + { |
60 | + // filename contains the name of the zip file as first element. This means |
61 | + // this is an old zip file where all data were in a directory named as the |
62 | + // file inside the zip file. |
63 | + // return the filename without the first element |
64 | + return filename.substr(basename_.length() + 1); |
65 | + } |
66 | + |
67 | + // seems to be a new zipfile without directory or a old zipfile was renamed |
68 | + if (*filename.begin() == '/') |
69 | + return filename.substr(1); |
70 | + return filename; |
71 | +} |
72 | + |
73 | +const zipFile& ZipFilesystem::ZipFile::write_handle() { |
74 | + open_for_zip(); |
75 | + return write_handle_; |
76 | +} |
77 | + |
78 | +const unzFile& ZipFilesystem::ZipFile::read_handle() { |
79 | + open_for_unzip(); |
80 | + return read_handle_; |
81 | +} |
82 | + |
83 | +const std::string& ZipFilesystem::ZipFile::path() const { |
84 | + return path_; |
85 | +} |
86 | + |
87 | /** |
88 | * Initialize the real file-system |
89 | */ |
90 | -ZipFilesystem::ZipFilesystem(const std::string & zipfile) |
91 | -: |
92 | -m_state (STATE_IDLE), |
93 | -m_zipfile (nullptr), |
94 | -m_unzipfile (nullptr), |
95 | -m_oldzip (false), |
96 | -m_zipfilename(zipfile), |
97 | -m_basenamezip(fs_filename(zipfile.c_str())), |
98 | -m_basename () |
99 | -{ |
100 | +ZipFilesystem::ZipFilesystem(const std::string& zipfile) |
101 | + : zip_file_(new ZipFile(zipfile)), basedir_in_zip_file_() { |
102 | // TODO(unknown): check OS permissions on whether the file is writable |
103 | } |
104 | |
105 | -/** |
106 | - * Cleanup code |
107 | - */ |
108 | -ZipFilesystem::~ZipFilesystem() |
109 | -{ |
110 | - m_close(); |
111 | +ZipFilesystem::ZipFilesystem(const std::shared_ptr<ZipFile>& shared_data, |
112 | + const std::string& basedir_in_zip_file) |
113 | + : zip_file_(shared_data), basedir_in_zip_file_(basedir_in_zip_file) { |
114 | +} |
115 | + |
116 | +ZipFilesystem::~ZipFilesystem() { |
117 | } |
118 | |
119 | /** |
120 | @@ -72,11 +143,9 @@ |
121 | * cross-platform way of doing this |
122 | */ |
123 | std::set<std::string> ZipFilesystem::list_directory(const std::string& path_in) { |
124 | - m_open_unzip(); |
125 | - |
126 | assert(path_in.size()); // prevent invalid read below |
127 | |
128 | - std::string path = m_basename; |
129 | + std::string path = basedir_in_zip_file_; |
130 | if (*path_in.begin() != '/') |
131 | path += "/"; |
132 | path += path_in; |
133 | @@ -85,18 +154,18 @@ |
134 | if (*path.begin() == '/') |
135 | path = path.substr(1); |
136 | |
137 | - unzCloseCurrentFile(m_unzipfile); |
138 | - unzGoToFirstFile(m_unzipfile); |
139 | + unzCloseCurrentFile(zip_file_->read_handle()); |
140 | + unzGoToFirstFile(zip_file_->read_handle()); |
141 | |
142 | unz_file_info file_info; |
143 | char filename_inzip[256]; |
144 | std::set<std::string> results; |
145 | for (;;) { |
146 | unzGetCurrentFileInfo |
147 | - (m_unzipfile, &file_info, filename_inzip, sizeof(filename_inzip), |
148 | + (zip_file_->read_handle(), &file_info, filename_inzip, sizeof(filename_inzip), |
149 | nullptr, 0, nullptr, 0); |
150 | |
151 | - std::string complete_filename = strip_basename(filename_inzip); |
152 | + std::string complete_filename = zip_file_->strip_basename(filename_inzip); |
153 | std::string filename = fs_filename(complete_filename.c_str()); |
154 | std::string filepath = |
155 | complete_filename.substr |
156 | @@ -108,9 +177,9 @@ |
157 | if |
158 | (('/' + path == filepath || path == filepath || path.length() == 1) |
159 | && filename.size()) |
160 | - results.insert(complete_filename.substr(m_basename.size())); |
161 | + results.insert(complete_filename.substr(basedir_in_zip_file_.size())); |
162 | |
163 | - if (unzGoToNextFile(m_unzipfile) == UNZ_END_OF_LIST_OF_FILE) |
164 | + if (unzGoToNextFile(zip_file_->read_handle()) == UNZ_END_OF_LIST_OF_FILE) |
165 | break; |
166 | } |
167 | return results; |
168 | @@ -120,18 +189,19 @@ |
169 | * Returns true if the given file exists, and false if it doesn't. |
170 | * Also returns false if the pathname is invalid |
171 | */ |
172 | -bool ZipFilesystem::file_exists(const std::string & path) { |
173 | +bool ZipFilesystem::file_exists(const std::string& path) { |
174 | try { |
175 | - m_open_unzip(); // TODO(unknown): check return value |
176 | + unzGoToFirstFile(zip_file_->read_handle()); |
177 | } catch (...) { |
178 | + // The zip file could not be opened. I guess this means 'path' does not |
179 | + // exist. |
180 | return false; |
181 | } |
182 | - unzGoToFirstFile(m_unzipfile); |
183 | unz_file_info file_info; |
184 | char filename_inzip[256]; |
185 | memset(filename_inzip, ' ', 256); |
186 | |
187 | - std::string path_in = m_basename + "/" + path; |
188 | + std::string path_in = basedir_in_zip_file_ + "/" + path; |
189 | |
190 | if (*path_in.begin() == '/') |
191 | path_in = path_in.substr(1); |
192 | @@ -140,10 +210,10 @@ |
193 | |
194 | for (;;) { |
195 | unzGetCurrentFileInfo |
196 | - (m_unzipfile, &file_info, filename_inzip, sizeof(filename_inzip), |
197 | + (zip_file_->read_handle(), &file_info, filename_inzip, sizeof(filename_inzip), |
198 | nullptr, 0, nullptr, 0); |
199 | |
200 | - std::string complete_filename = strip_basename(filename_inzip); |
201 | + std::string complete_filename = zip_file_->strip_basename(filename_inzip); |
202 | |
203 | if (*complete_filename.rbegin() == '/') |
204 | complete_filename.resize(complete_filename.size() - 1); |
205 | @@ -151,7 +221,7 @@ |
206 | if (path_in == complete_filename) |
207 | return true; |
208 | |
209 | - if (unzGoToNextFile(m_unzipfile) == UNZ_END_OF_LIST_OF_FILE) |
210 | + if (unzGoToNextFile(zip_file_->read_handle()) == UNZ_END_OF_LIST_OF_FILE) |
211 | break; |
212 | } |
213 | return false; |
214 | @@ -170,7 +240,7 @@ |
215 | char filename_inzip[256]; |
216 | |
217 | unzGetCurrentFileInfo |
218 | - (m_unzipfile, &file_info, filename_inzip, sizeof(filename_inzip), |
219 | + (zip_file_->read_handle(), &file_info, filename_inzip, sizeof(filename_inzip), |
220 | nullptr, 0, nullptr, 0); |
221 | |
222 | return filename_inzip[strlen(filename_inzip) - 1] == '/'; |
223 | @@ -180,8 +250,6 @@ |
224 | * Create a sub filesystem out of this filesystem |
225 | */ |
226 | FileSystem * ZipFilesystem::make_sub_file_system(const std::string & path) { |
227 | - m_open_unzip(); |
228 | - |
229 | if (!file_exists(path)) { |
230 | throw wexception("ZipFilesystem::make_sub_file_system: The path does not exist."); |
231 | } |
232 | @@ -189,17 +257,11 @@ |
233 | throw wexception("ZipFilesystem::make_sub_file_system: The path needs to be a directory."); |
234 | } |
235 | |
236 | - m_close(); |
237 | - |
238 | std::string localpath = path; |
239 | - |
240 | - if (*localpath.begin() == '/') |
241 | + if (*localpath.begin() == '/') { |
242 | localpath = localpath.substr(1); |
243 | - |
244 | - ZipFilesystem * newfs = new ZipFilesystem(m_zipfilename); |
245 | - newfs->m_basename = m_basename + "/" + localpath; |
246 | - |
247 | - return newfs; |
248 | + } |
249 | + return new ZipFilesystem(zip_file_, basedir_in_zip_file_ + "/" + localpath); |
250 | } |
251 | |
252 | /** |
253 | @@ -217,23 +279,15 @@ |
254 | if (type != FileSystem::DIR) |
255 | throw ZipOperationError |
256 | ("ZipFilesystem::create_sub_file_system", |
257 | - path, m_zipfilename, |
258 | + path, zip_file_->path(), |
259 | "can not create ZipFilesystem inside another ZipFilesystem"); |
260 | |
261 | ensure_directory_exists(path); |
262 | |
263 | - m_close(); |
264 | - |
265 | std::string localpath = path; |
266 | - |
267 | if (*localpath.begin() == '/') |
268 | localpath = localpath.substr(1); |
269 | - |
270 | - ZipFilesystem * newfs = new ZipFilesystem(*this); |
271 | - |
272 | - newfs->m_basename = m_basename + "/" + localpath; |
273 | - |
274 | - return newfs; |
275 | + return new ZipFilesystem(zip_file_, basedir_in_zip_file_ + "/" + localpath); |
276 | } |
277 | /** |
278 | * Remove a number of files |
279 | @@ -243,7 +297,7 @@ |
280 | throw ZipOperationError |
281 | ("ZipFilesystem::unlink", |
282 | filename, |
283 | - m_zipfilename, |
284 | + zip_file_->path(), |
285 | "unlinking is not supported inside zipfiles"); |
286 | } |
287 | |
288 | @@ -267,8 +321,6 @@ |
289 | * if either ondir or otherdir is missing |
290 | */ |
291 | void ZipFilesystem::make_directory(const std::string & dirname) { |
292 | - m_open_zip(); |
293 | - |
294 | zip_fileinfo zi; |
295 | |
296 | zi.tmz_date.tm_sec = zi.tmz_date.tm_min = zi.tmz_date.tm_hour = |
297 | @@ -277,7 +329,7 @@ |
298 | zi.internal_fa = 0; |
299 | zi.external_fa = 0; |
300 | |
301 | - std::string complete_filename = m_basename; |
302 | + std::string complete_filename = basedir_in_zip_file_; |
303 | complete_filename += "/"; |
304 | complete_filename += dirname; |
305 | |
306 | @@ -285,21 +337,9 @@ |
307 | if (*complete_filename.rbegin() != '/') |
308 | complete_filename += '/'; |
309 | |
310 | - switch |
311 | - (zipOpenNewFileInZip3 |
312 | - (m_zipfile, |
313 | - complete_filename.c_str(), |
314 | - &zi, |
315 | - nullptr, 0, nullptr, 0, nullptr /* comment*/, |
316 | - Z_DEFLATED, |
317 | - Z_BEST_COMPRESSION, |
318 | - 0, |
319 | - -MAX_WBITS, |
320 | - DEF_MEM_LEVEL, |
321 | - Z_DEFAULT_STRATEGY, |
322 | - nullptr, |
323 | - 0)) |
324 | - { |
325 | + switch (zipOpenNewFileInZip3(zip_file_->write_handle(), complete_filename.c_str(), &zi, nullptr, |
326 | + 0, nullptr, 0, nullptr /* comment*/, Z_DEFLATED, Z_BEST_COMPRESSION, |
327 | + 0, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, nullptr, 0)) { |
328 | case ZIP_OK: |
329 | break; |
330 | case ZIP_ERRNO: |
331 | @@ -309,8 +349,7 @@ |
332 | throw FileError |
333 | ("ZipFilesystem::make_directory", complete_filename); |
334 | } |
335 | - |
336 | - zipCloseFileInZip(m_zipfile); |
337 | + zipCloseFileInZip(zip_file_->write_handle()); |
338 | } |
339 | |
340 | /** |
341 | @@ -322,34 +361,34 @@ |
342 | throw ZipOperationError |
343 | ("ZipFilesystem::load", |
344 | fname, |
345 | - m_zipfilename, |
346 | + zip_file_->path(), |
347 | "could not open file from zipfile"); |
348 | |
349 | char buffer[1024]; |
350 | size_t totallen = 0; |
351 | - unzOpenCurrentFile(m_unzipfile); |
352 | + unzOpenCurrentFile(zip_file_->read_handle()); |
353 | for (;;) { |
354 | const int32_t len = |
355 | - unzReadCurrentFile(m_unzipfile, buffer, sizeof(buffer)); |
356 | + unzReadCurrentFile(zip_file_->read_handle(), buffer, sizeof(buffer)); |
357 | if (len == 0) |
358 | break; |
359 | if (len < 0) { |
360 | - unzCloseCurrentFile(m_unzipfile); |
361 | + unzCloseCurrentFile(zip_file_->read_handle()); |
362 | const std::string errormessage = (boost::format("read error %i") % len).str(); |
363 | throw ZipOperationError |
364 | - ("ZipFilesystem::load", fname, m_zipfilename, errormessage.c_str()); |
365 | + ("ZipFilesystem::load", fname, zip_file_->path(), errormessage.c_str()); |
366 | } |
367 | |
368 | totallen += len; |
369 | } |
370 | - unzCloseCurrentFile(m_unzipfile); |
371 | + unzCloseCurrentFile(zip_file_->read_handle()); |
372 | |
373 | void * const result = malloc(totallen + 1); |
374 | if (!result) |
375 | throw std::bad_alloc(); |
376 | - unzOpenCurrentFile(m_unzipfile); |
377 | - unzReadCurrentFile(m_unzipfile, result, totallen); |
378 | - unzCloseCurrentFile(m_unzipfile); |
379 | + unzOpenCurrentFile(zip_file_->read_handle()); |
380 | + unzReadCurrentFile(zip_file_->read_handle(), result, totallen); |
381 | + unzCloseCurrentFile(zip_file_->read_handle()); |
382 | |
383 | static_cast<uint8_t *>(result)[totallen] = 0; |
384 | length = totallen; |
385 | @@ -367,36 +406,27 @@ |
386 | std::string filename = fname; |
387 | std::replace(filename.begin(), filename.end(), '\\', '/'); |
388 | |
389 | - m_open_zip(); |
390 | - |
391 | zip_fileinfo zi; |
392 | - |
393 | zi.tmz_date.tm_sec = zi.tmz_date.tm_min = zi.tmz_date.tm_hour = |
394 | zi.tmz_date.tm_mday = zi.tmz_date.tm_mon = zi.tmz_date.tm_year = 0; |
395 | zi.dosDate = 0; |
396 | zi.internal_fa = 0; |
397 | zi.external_fa = 0; |
398 | |
399 | - std::string complete_filename = m_basename + "/" + filename; |
400 | + std::string complete_filename = basedir_in_zip_file_ + "/" + filename; |
401 | |
402 | // create file |
403 | - switch |
404 | - (zipOpenNewFileInZip3 |
405 | - (m_zipfile, complete_filename.c_str(), &zi, |
406 | - nullptr, 0, nullptr, 0, nullptr /* comment*/, |
407 | - Z_DEFLATED, |
408 | - Z_BEST_COMPRESSION, 0, |
409 | - -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, |
410 | - nullptr, 0)) |
411 | - { |
412 | + switch (zipOpenNewFileInZip3(zip_file_->write_handle(), complete_filename.c_str(), &zi, nullptr, |
413 | + 0, nullptr, 0, nullptr /* comment*/, Z_DEFLATED, Z_BEST_COMPRESSION, |
414 | + 0, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, nullptr, 0)) { |
415 | case ZIP_OK: |
416 | break; |
417 | default: |
418 | - throw ZipOperationError |
419 | - ("ZipFilesystem::write", complete_filename, m_zipfilename); |
420 | + throw ZipOperationError( |
421 | + "ZipFilesystem::write", complete_filename, zip_file_->path()); |
422 | } |
423 | |
424 | - switch (zipWriteInFileInZip (m_zipfile, data, length)) { |
425 | + switch (zipWriteInFileInZip (zip_file_->write_handle(), data, length)) { |
426 | case ZIP_OK: |
427 | break; |
428 | case ZIP_ERRNO: |
429 | @@ -407,22 +437,80 @@ |
430 | ("ZipFilesystem::write", complete_filename); |
431 | } |
432 | |
433 | - zipCloseFileInZip(m_zipfile); |
434 | -} |
435 | - |
436 | -// |
437 | -// Stream Read |
438 | -// |
439 | - |
440 | -ZipFilesystem::ZipStreamRead::ZipStreamRead(zipFile file, ZipFilesystem* zipfs) |
441 | -: m_unzipfile(file), m_zipfs(zipfs) {} |
442 | + zipCloseFileInZip(zip_file_->write_handle()); |
443 | +} |
444 | + |
445 | +StreamRead* ZipFilesystem::open_stream_read(const std::string& fname) { |
446 | + if (!file_exists(fname.c_str()) || is_directory(fname.c_str())) |
447 | + throw ZipOperationError |
448 | + ("ZipFilesystem::load", |
449 | + fname, |
450 | + zip_file_->path(), |
451 | + "could not open file from zipfile"); |
452 | + |
453 | + int32_t method; |
454 | + int result = unzOpenCurrentFile3(zip_file_->read_handle(), &method, nullptr, 1, nullptr); |
455 | + switch (result) { |
456 | + case ZIP_OK: |
457 | + break; |
458 | + default: |
459 | + throw ZipOperationError |
460 | + ("ZipFilesystem: Failed to open streamwrite", fname, zip_file_->path()); |
461 | + } |
462 | + return new ZipStreamRead(zip_file_); |
463 | +} |
464 | + |
465 | +StreamWrite * ZipFilesystem::open_stream_write(const std::string & fname) { |
466 | + zip_fileinfo zi; |
467 | + |
468 | + zi.tmz_date.tm_sec = zi.tmz_date.tm_min = zi.tmz_date.tm_hour = |
469 | + zi.tmz_date.tm_mday = zi.tmz_date.tm_mon = zi.tmz_date.tm_year = 0; |
470 | + zi.dosDate = 0; |
471 | + zi.internal_fa = 0; |
472 | + zi.external_fa = 0; |
473 | + |
474 | + std::string complete_filename = basedir_in_zip_file_ + "/" + fname; |
475 | + // create file |
476 | + switch |
477 | + (zipOpenNewFileInZip3 |
478 | + (zip_file_->write_handle(), complete_filename.c_str(), &zi, |
479 | + nullptr, 0, nullptr, 0, nullptr /* comment*/, |
480 | + Z_DEFLATED, |
481 | + Z_BEST_COMPRESSION, 0, |
482 | + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, |
483 | + nullptr, 0)) |
484 | + { |
485 | + case ZIP_OK: |
486 | + break; |
487 | + default: |
488 | + throw ZipOperationError |
489 | + ("ZipFilesystem: Failed to open streamwrite", complete_filename, zip_file_->path()); |
490 | + } |
491 | + return new ZipStreamWrite(zip_file_); |
492 | +} |
493 | + |
494 | +void ZipFilesystem::fs_rename(const std::string &, const std::string &) { |
495 | + throw wexception("rename inside zip FS is not implemented yet"); |
496 | +} |
497 | + |
498 | +unsigned long long ZipFilesystem::disk_space() { |
499 | + return 0; |
500 | +} |
501 | + |
502 | +std::string ZipFilesystem::get_basename() { |
503 | + return zip_file_->path(); |
504 | +} |
505 | + |
506 | +ZipFilesystem::ZipStreamRead::ZipStreamRead(const std::shared_ptr<ZipFile>& shared_data) |
507 | + : zip_file_(shared_data) { |
508 | +} |
509 | + |
510 | ZipFilesystem::ZipStreamRead::~ZipStreamRead() { |
511 | - m_zipfs->m_close(); |
512 | } |
513 | |
514 | size_t ZipFilesystem::ZipStreamRead::data(void* read_data, size_t bufsize) |
515 | { |
516 | - int copied = unzReadCurrentFile(m_unzipfile, read_data, bufsize); |
517 | + int copied = unzReadCurrentFile(zip_file_->read_handle(), read_data, bufsize); |
518 | if (copied < 0) { |
519 | throw new DataError("Failed to read from zip file"); |
520 | } |
521 | @@ -431,46 +519,22 @@ |
522 | } |
523 | return copied; |
524 | } |
525 | + |
526 | bool ZipFilesystem::ZipStreamRead::end_of_file() const |
527 | { |
528 | - return unzReadCurrentFile(m_unzipfile, nullptr, 1) == 0; |
529 | -} |
530 | - |
531 | -StreamRead* ZipFilesystem::open_stream_read(const std::string& fname) { |
532 | - if (!file_exists(fname.c_str()) || is_directory(fname.c_str())) |
533 | - throw ZipOperationError |
534 | - ("ZipFilesystem::load", |
535 | - fname, |
536 | - m_zipfilename, |
537 | - "could not open file from zipfile"); |
538 | - |
539 | - int32_t method; |
540 | - m_open_unzip(); |
541 | - int result = unzOpenCurrentFile3(m_unzipfile, &method, nullptr, 1, nullptr); |
542 | - switch (result) { |
543 | - case ZIP_OK: |
544 | - break; |
545 | - default: |
546 | - throw ZipOperationError |
547 | - ("ZipFilesystem: Failed to open streamwrite", fname, m_zipfilename); |
548 | - } |
549 | - return new ZipStreamRead(m_unzipfile, this); |
550 | -} |
551 | - |
552 | -// |
553 | -// Stream write |
554 | -// |
555 | - |
556 | -ZipFilesystem::ZipStreamWrite::ZipStreamWrite(zipFile file, ZipFilesystem* zipfs) |
557 | -: m_zipfile(file), m_zipfs(zipfs) {} |
558 | -ZipFilesystem::ZipStreamWrite::~ZipStreamWrite() |
559 | -{ |
560 | - m_zipfs->m_close(); |
561 | + return unzReadCurrentFile(zip_file_->read_handle(), nullptr, 1) == 0; |
562 | +} |
563 | + |
564 | +ZipFilesystem::ZipStreamWrite::ZipStreamWrite(const std::shared_ptr<ZipFile>& shared_data) |
565 | + : zip_file_(shared_data) { |
566 | +} |
567 | + |
568 | +ZipFilesystem::ZipStreamWrite::~ZipStreamWrite() { |
569 | } |
570 | |
571 | void ZipFilesystem::ZipStreamWrite::data(const void* const write_data, const size_t size) |
572 | { |
573 | - int result = zipWriteInFileInZip(m_zipfile, write_data, size); |
574 | + int result = zipWriteInFileInZip(zip_file_->write_handle(), write_data, size); |
575 | switch (result) { |
576 | case ZIP_OK: |
577 | break; |
578 | @@ -478,111 +542,3 @@ |
579 | throw wexception("Failed to write into zipfile"); |
580 | } |
581 | } |
582 | - |
583 | -StreamWrite * ZipFilesystem::open_stream_write(const std::string & fname) { |
584 | - m_open_zip(); |
585 | - |
586 | - zip_fileinfo zi; |
587 | - |
588 | - zi.tmz_date.tm_sec = zi.tmz_date.tm_min = zi.tmz_date.tm_hour = |
589 | - zi.tmz_date.tm_mday = zi.tmz_date.tm_mon = zi.tmz_date.tm_year = 0; |
590 | - zi.dosDate = 0; |
591 | - zi.internal_fa = 0; |
592 | - zi.external_fa = 0; |
593 | - |
594 | - std::string complete_filename = m_basename + "/" + fname; |
595 | - // create file |
596 | - switch |
597 | - (zipOpenNewFileInZip3 |
598 | - (m_zipfile, complete_filename.c_str(), &zi, |
599 | - nullptr, 0, nullptr, 0, nullptr /* comment*/, |
600 | - Z_DEFLATED, |
601 | - Z_BEST_COMPRESSION, 0, |
602 | - -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, |
603 | - nullptr, 0)) |
604 | - { |
605 | - case ZIP_OK: |
606 | - break; |
607 | - default: |
608 | - throw ZipOperationError |
609 | - ("ZipFilesystem: Failed to open streamwrite", complete_filename, m_zipfilename); |
610 | - } |
611 | - return new ZipStreamWrite(m_zipfile, this); |
612 | -} |
613 | - |
614 | - |
615 | -/** |
616 | - * Private Functions below |
617 | - */ |
618 | -void ZipFilesystem::m_close() { |
619 | - if (m_state == STATE_ZIPPING) |
620 | - zipClose(m_zipfile, nullptr); |
621 | - else if (m_state == STATE_UNZIPPPING) |
622 | - unzClose(m_unzipfile); |
623 | - |
624 | - m_state = STATE_IDLE; |
625 | -} |
626 | - |
627 | -/** |
628 | - * Open a zipfile for compressing |
629 | - */ |
630 | -void ZipFilesystem::m_open_zip() { |
631 | - if (m_state == STATE_ZIPPING) |
632 | - return; |
633 | - |
634 | - m_close(); |
635 | - |
636 | - m_zipfile = zipOpen(m_zipfilename.c_str(), APPEND_STATUS_ADDINZIP); |
637 | - if (!m_zipfile) { |
638 | - // Couldn't open for append, so create new. |
639 | - m_zipfile = zipOpen(m_zipfilename.c_str(), APPEND_STATUS_CREATE); |
640 | - } |
641 | - |
642 | - m_state = STATE_ZIPPING; |
643 | -} |
644 | - |
645 | -/** |
646 | - * Open a zipfile for extraction |
647 | - * \throw FileTypeError |
648 | - */ |
649 | -void ZipFilesystem::m_open_unzip() { |
650 | - if (m_state == STATE_UNZIPPPING) |
651 | - return; |
652 | - |
653 | - m_close(); |
654 | - |
655 | - m_unzipfile = unzOpen(m_zipfilename.c_str()); |
656 | - if (!m_unzipfile) |
657 | - throw FileTypeError |
658 | - ("ZipFilesystem::m_open_unzip", m_zipfilename, "not a .zip file"); |
659 | - |
660 | - m_state = STATE_UNZIPPPING; |
661 | -} |
662 | - |
663 | -void ZipFilesystem::fs_rename(const std::string &, const std::string &) { |
664 | - throw wexception("rename inside zip FS is not implemented yet"); |
665 | -} |
666 | - |
667 | -unsigned long long ZipFilesystem::disk_space() { |
668 | - return 0; |
669 | -} |
670 | - |
671 | -std::string ZipFilesystem::strip_basename(std::string filename) |
672 | -{ |
673 | - |
674 | - if (filename.compare(0, m_basenamezip.length(), m_basenamezip) == 0) |
675 | - { |
676 | - // filename contains the name of the zip file as first element. This means |
677 | - // this is an old zip file where all data were in a directory named as the |
678 | - // file inside the zip file. |
679 | - // return the filename without the first element |
680 | - m_oldzip = true; |
681 | - return filename.substr(m_basenamezip.length() + 1); |
682 | - } |
683 | - |
684 | - // seems to be a new zipfile without directory or a old zipfile was renamed |
685 | - m_oldzip = false; |
686 | - if (*filename.begin() == '/') |
687 | - return filename.substr(1); |
688 | - return filename; |
689 | -} |
690 | |
691 | === modified file 'src/io/filesystem/zip_filesystem.h' |
692 | --- src/io/filesystem/zip_filesystem.h 2014-09-29 13:30:46 +0000 |
693 | +++ src/io/filesystem/zip_filesystem.h 2016-01-17 21:52:36 +0000 |
694 | @@ -21,6 +21,7 @@ |
695 | #define WL_IO_FILESYSTEM_ZIP_FILESYSTEM_H |
696 | |
697 | #include <cstring> |
698 | +#include <memory> |
699 | #include <string> |
700 | |
701 | #include "base/macros.h" |
702 | @@ -63,50 +64,87 @@ |
703 | |
704 | static FileSystem * create_from_directory(const std::string & directory); |
705 | |
706 | - std::string get_basename() override {return m_zipfilename;} |
707 | - |
708 | -protected: |
709 | - void m_open_unzip(); |
710 | - void m_open_zip(); |
711 | - void m_close(); |
712 | - std::string strip_basename(std::string); |
713 | - |
714 | - enum State { |
715 | - STATE_IDLE, |
716 | - STATE_ZIPPING, |
717 | - STATE_UNZIPPPING |
718 | + std::string get_basename() override; |
719 | + |
720 | +private: |
721 | + enum class State {kIdle, kZipping, kUnzipping}; |
722 | + |
723 | + // All zip filesystems that use the same zip file have a shared |
724 | + // state, which is represented in this struct. This is usually |
725 | + // shared between the root file system, plus every filesystem generated |
726 | + // through 'make_sub_file_system'. |
727 | + class ZipFile { |
728 | + public: |
729 | + ZipFile(const std::string& zipfile); |
730 | + |
731 | + // Calls 'close()'. |
732 | + ~ZipFile(); |
733 | + |
734 | + // Make 'filename' into a relative part, dealing with legacy Widelands |
735 | + // zip file format. |
736 | + std::string strip_basename(const std::string& filename); |
737 | + |
738 | + // Full path to the zip file. |
739 | + const std::string& path() const; |
740 | + |
741 | + // Closes the file if it is open, reopens it for writing, and |
742 | + // returns the minizip handle. |
743 | + const zipFile& write_handle(); |
744 | + |
745 | + // Closes the file if it is open, reopens it for reading, and returns the |
746 | + // minizip handle. |
747 | + const unzFile& read_handle(); |
748 | + |
749 | + private: |
750 | + // Closes 'path_' and reopens it for unzipping (read). |
751 | + void open_for_unzip(); |
752 | + |
753 | + // Closes 'path_' and reopens it for zipping (write). |
754 | + void open_for_zip(); |
755 | + |
756 | + // Closes 'path_' if it is opened. |
757 | + void close(); |
758 | + |
759 | + State state_; |
760 | + |
761 | + // E.g. "path/to/filename.zip" |
762 | + std::string path_; |
763 | + |
764 | + // E.g. "filename.zip" |
765 | + std::string basename_; |
766 | + |
767 | + // File handles for zipping and unzipping. |
768 | + zipFile write_handle_; |
769 | + unzFile read_handle_; |
770 | }; |
771 | |
772 | - State m_state; |
773 | - zipFile m_zipfile; |
774 | - unzFile m_unzipfile; |
775 | - /// if true data is in a directory named as the zipfile. This is set by |
776 | - /// strip_basename() |
777 | - bool m_oldzip; |
778 | - std::string m_zipfilename; |
779 | - std::string m_basenamezip; |
780 | - std::string m_basename; |
781 | - |
782 | struct ZipStreamRead : StreamRead { |
783 | - explicit ZipStreamRead(zipFile file, ZipFilesystem* zipfs); |
784 | + explicit ZipStreamRead(const std::shared_ptr<ZipFile>& shared_data); |
785 | virtual ~ZipStreamRead(); |
786 | size_t data(void* data, size_t bufsize) override; |
787 | bool end_of_file() const override; |
788 | private: |
789 | - zipFile m_unzipfile; |
790 | - ZipFilesystem* m_zipfs; |
791 | + std::shared_ptr<ZipFile> zip_file_; |
792 | }; |
793 | + |
794 | struct ZipStreamWrite : StreamWrite { |
795 | - explicit ZipStreamWrite(zipFile file, ZipFilesystem* zipfs); |
796 | + explicit ZipStreamWrite(const std::shared_ptr<ZipFile>& shared_data); |
797 | virtual ~ZipStreamWrite(); |
798 | void data(const void* const data, size_t size) override; |
799 | private: |
800 | - zipFile m_zipfile; |
801 | - ZipFilesystem* m_zipfs; |
802 | + std::shared_ptr<ZipFile> zip_file_; |
803 | }; |
804 | |
805 | + // Used for creating sub filesystems. |
806 | + ZipFilesystem(const std::shared_ptr<ZipFile>& shared_data, const std::string& basedir_in_zip_file); |
807 | + |
808 | + // The data shared between all zip filesystems with the same |
809 | + // underlying zip file. |
810 | + std::shared_ptr<ZipFile> zip_file_; |
811 | + |
812 | + // If we are a sub filesystem, this points to our base |
813 | + // directory, otherwise it is the empty string. |
814 | + std::string basedir_in_zip_file_; |
815 | }; |
816 | |
817 | - |
818 | - |
819 | #endif // end of include guard: WL_IO_FILESYSTEM_ZIP_FILESYSTEM_H |
Testers: This should be a refactoring only to make sure we are not broken by c++14 (which deprecates our inproper use of Copy construction in this case). No noticeable changes in behavior.