ManaPlus
fszip.cpp
Go to the documentation of this file.
1 /*
2  * The ManaPlus Client
3  * Copyright (C) 2013-2019 The ManaPlus Developers
4  * Copyright (C) 2019-2021 Andrei Karas
5  *
6  * This file is part of The ManaPlus Client.
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program. If not, see <http://www.gnu.org/licenses/>.
20  */
21 
22 #include "fs/virtfs/fszip.h"
23 
24 #include "fs/virtfs/file.h"
25 #include "fs/virtfs/fsfuncs.h"
26 #include "fs/virtfs/fsziprwops.h"
27 #include "fs/virtfs/list.h"
28 #include "fs/virtfs/zipentry.h"
29 #include "fs/virtfs/zipreader.h"
31 
32 #include "utils/cast.h"
33 #include "utils/checkutils.h"
34 #include "utils/foreach.h"
35 #include "utils/stringutils.h"
36 
37 #include "debug.h"
38 
39 extern const char *dirSeparator;
40 
41 namespace
42 {
44 } // namespace
45 
46 namespace VirtFs
47 {
48 
49 namespace FsZip
50 {
52  {
53  return &funcs;
54  }
55 
56  void deinit()
57  {
58  }
59 
60  void init()
61  {
62  initFuncs(&funcs);
63  }
64 
65  void initFuncs(FsFuncs *restrict const ptr)
66  {
67  ptr->close = &FsZip::close;
68  ptr->read = &FsZip::read;
69  ptr->write = &FsZip::write;
70  ptr->fileLength = &FsZip::fileLength;
71  ptr->tell = &FsZip::tell;
72  ptr->seek = &FsZip::seek;
73  ptr->eof = &FsZip::eof;
74  ptr->exists = &FsZip::exists;
75  ptr->getRealDir = &FsZip::getRealDir;
76  ptr->enumerate = &FsZip::enumerate;
77  ptr->isDirectory = &FsZip::isDirectory;
78  ptr->openRead = &FsZip::openRead;
79  ptr->openWrite = &FsZip::openWrite;
80  ptr->openAppend = &FsZip::openAppend;
81  ptr->loadFile = &FsZip::loadFile;
82  ptr->getFiles = &FsZip::getFiles;
83  ptr->getFilesWithDir = &FsZip::getFilesWithDir;
84  ptr->getDirs = &FsZip::getDirs;
85  ptr->rwops_seek = &FsZip::rwops_seek;
86  ptr->rwops_read = &FsZip::rwops_read;
87  ptr->rwops_write = &FsZip::rwops_write;
88  ptr->rwops_close = &FsZip::rwops_close;
89 #ifdef USE_SDL2
90  ptr->rwops_size = &FsZip::rwops_size;
91 #endif // USE_SDL2
92  }
93 
94  bool getRealDir(FsEntry *restrict const entry,
95  std::string filename,
96  std::string dirName,
97  std::string &realDir)
98  {
99  ZipEntry *const zipEntry = static_cast<ZipEntry*>(entry);
100  std::string subDir = zipEntry->subDir;
101  if (!subDir.empty())
102  {
103  filename = pathJoin(subDir, filename);
104  dirName = pathJoin(subDir, dirName);
105  }
106  FOR_EACH (STD_VECTOR<ZipLocalHeader*>::const_iterator,
107  it2,
108  zipEntry->mHeaders)
109  {
110  if ((*it2)->fileName == filename)
111  {
112  realDir = entry->root;
113  return true;
114  }
115  }
116  FOR_EACH (STD_VECTOR<std::string>::const_iterator,
117  it2,
118  zipEntry->mDirs)
119  {
120  if (*it2 == dirName)
121  {
122  realDir = entry->root;
123  return true;
124  }
125  }
126  return false;
127  }
128 
129  bool exists(FsEntry *restrict const entry,
130  std::string filename,
131  std::string dirName)
132  {
133  ZipEntry *const zipEntry = static_cast<ZipEntry*>(entry);
134  std::string subDir = zipEntry->subDir;
135  if (!subDir.empty())
136  {
137  filename = pathJoin(subDir, filename);
138  dirName = pathJoin(subDir, dirName);
139  }
140  FOR_EACH (STD_VECTOR<ZipLocalHeader*>::const_iterator,
141  it2,
142  zipEntry->mHeaders)
143  {
144  if ((*it2)->fileName == filename)
145  return true;
146  }
147  FOR_EACH (STD_VECTOR<std::string>::const_iterator,
148  it2,
149  zipEntry->mDirs)
150  {
151  if (*it2 == dirName)
152  return true;
153  }
154  return false;
155  }
156 
157  void enumerate(FsEntry *restrict const entry,
158  std::string dirName,
159  StringVect &names)
160  {
161  ZipEntry *const zipEntry = static_cast<ZipEntry*>(entry);
162  const std::string subDir = zipEntry->subDir;
163  if (!subDir.empty())
164  dirName = pathJoin(subDir, dirName);
165  if (dirName == dirSeparator)
166  {
167  FOR_EACH (STD_VECTOR<ZipLocalHeader*>::const_iterator,
168  it2,
169  zipEntry->mHeaders)
170  {
171  ZipLocalHeader *const header = *it2;
172  std::string fileName = header->fileName;
173  // skip subdirs from enumeration
174  const size_t idx = fileName.find(dirSeparator);
175  if (idx != std::string::npos)
176  fileName.erase(idx);
177  bool found(false);
178  FOR_EACH (StringVectCIter, itn, names)
179  {
180  if (*itn == fileName)
181  {
182  found = true;
183  break;
184  }
185  }
186  if (found == false)
187  names.push_back(fileName);
188  }
189  }
190  else
191  {
192  FOR_EACH (STD_VECTOR<ZipLocalHeader*>::const_iterator,
193  it2,
194  zipEntry->mHeaders)
195  {
196  ZipLocalHeader *const header = *it2;
197  std::string fileName = header->fileName;
198  if (findCutFirst(fileName, dirName) == true)
199  {
200  // skip subdirs from enumeration
201  const size_t idx = fileName.find(dirSeparator);
202  if (idx != std::string::npos)
203  fileName.erase(idx);
204  bool found(false);
205  FOR_EACH (StringVectCIter, itn, names)
206  {
207  if (*itn == fileName)
208  {
209  found = true;
210  break;
211  }
212  }
213  if (found == false)
214  names.push_back(fileName);
215  }
216  }
217  }
218  }
219 
220  void getFiles(FsEntry *restrict const entry,
221  std::string dirName,
222  StringVect &names)
223  {
224  ZipEntry *const zipEntry = static_cast<ZipEntry*>(entry);
225  const std::string subDir = zipEntry->subDir;
226  if (!subDir.empty())
227  dirName = pathJoin(subDir, dirName);
228  if (dirName == dirSeparator)
229  {
230  FOR_EACH (STD_VECTOR<ZipLocalHeader*>::const_iterator,
231  it2,
232  zipEntry->mHeaders)
233  {
234  ZipLocalHeader *const header = *it2;
235  std::string fileName = header->fileName;
236  // skip subdirs from enumeration
237  const size_t idx = fileName.find(dirSeparator);
238  if (idx != std::string::npos)
239  fileName.erase(idx);
240  bool found(false);
241  FOR_EACH (StringVectCIter, itn, names)
242  {
243  if (*itn == fileName)
244  {
245  found = true;
246  break;
247  }
248  }
249  if (found == false)
250  {
251  std::string dirName2 = pathJoin(dirName, fileName);
252  if (findLast(dirName2, std::string(dirSeparator)) == false)
253  dirName2 += dirSeparator;
254  FOR_EACH (STD_VECTOR<std::string>::const_iterator,
255  it,
256  zipEntry->mDirs)
257  {
258  if (*it == dirName2)
259  {
260  found = true;
261  break;
262  }
263  }
264  if (found == false)
265  names.push_back(fileName);
266  }
267  }
268  }
269  else
270  {
271  FOR_EACH (STD_VECTOR<ZipLocalHeader*>::const_iterator,
272  it2,
273  zipEntry->mHeaders)
274  {
275  ZipLocalHeader *const header = *it2;
276  std::string fileName = header->fileName;
277  if (findCutFirst(fileName, dirName) == true)
278  {
279  // skip subdirs from enumeration
280  const size_t idx = fileName.find(dirSeparator);
281  if (idx != std::string::npos)
282  fileName.erase(idx);
283  bool found(false);
284  FOR_EACH (StringVectCIter, itn, names)
285  {
286  if (*itn == fileName)
287  {
288  found = true;
289  break;
290  }
291  }
292  if (found == false)
293  {
294  std::string dirName2 = pathJoin(dirName, fileName);
295  if (findLast(dirName2, std::string(dirSeparator)) ==
296  false)
297  {
298  dirName2 += dirSeparator;
299  }
300  FOR_EACH (STD_VECTOR<std::string>::const_iterator,
301  it,
302  zipEntry->mDirs)
303  {
304  if (*it == dirName2)
305  {
306  found = true;
307  break;
308  }
309  }
310  if (found == false)
311  names.push_back(fileName);
312  }
313  }
314  }
315  }
316  }
317 
318  void getFilesWithDir(FsEntry *restrict const entry,
319  const std::string &dirName,
320  StringVect &names)
321  {
322  ZipEntry *const zipEntry = static_cast<ZipEntry*>(entry);
323  const std::string subDir = zipEntry->subDir;
324  std::string dirNameFull;
325  if (!subDir.empty())
326  dirNameFull = pathJoin(subDir, dirName);
327  else
328  dirNameFull = dirName;
329  if (dirNameFull == dirSeparator)
330  {
331  FOR_EACH (STD_VECTOR<ZipLocalHeader*>::const_iterator,
332  it2,
333  zipEntry->mHeaders)
334  {
335  ZipLocalHeader *const header = *it2;
336  std::string fileName = header->fileName;
337  // skip subdirs from enumeration
338  const size_t idx = fileName.find(dirSeparator);
339  if (idx != std::string::npos)
340  fileName.erase(idx);
341  bool found(false);
342  FOR_EACH (StringVectCIter, itn, names)
343  {
344  if (*itn == fileName)
345  {
346  found = true;
347  break;
348  }
349  }
350  if (found == false)
351  {
352  std::string dirName2 = pathJoin(dirNameFull, fileName);
353  if (findLast(dirName2, std::string(dirSeparator)) == false)
354  dirName2 += dirSeparator;
355  FOR_EACH (STD_VECTOR<std::string>::const_iterator,
356  it,
357  zipEntry->mDirs)
358  {
359  if (*it == dirName2)
360  {
361  found = true;
362  break;
363  }
364  }
365  if (found == false)
366  names.push_back(pathJoin(dirName, fileName));
367  }
368  }
369  }
370  else
371  {
372  FOR_EACH (STD_VECTOR<ZipLocalHeader*>::const_iterator,
373  it2,
374  zipEntry->mHeaders)
375  {
376  ZipLocalHeader *const header = *it2;
377  std::string fileName = header->fileName;
378  if (findCutFirst(fileName, dirNameFull) == true)
379  {
380  // skip subdirs from enumeration
381  const size_t idx = fileName.find(dirSeparator);
382  if (idx != std::string::npos)
383  fileName.erase(idx);
384  bool found(false);
385  FOR_EACH (StringVectCIter, itn, names)
386  {
387  if (*itn == fileName)
388  {
389  found = true;
390  break;
391  }
392  }
393  if (found == false)
394  {
395  std::string dirName2 = pathJoin(dirNameFull, fileName);
396  if (findLast(dirName2, std::string(dirSeparator)) ==
397  false)
398  {
399  dirName2 += dirSeparator;
400  }
401  FOR_EACH (STD_VECTOR<std::string>::const_iterator,
402  it,
403  zipEntry->mDirs)
404  {
405  if (*it == dirName2)
406  {
407  found = true;
408  break;
409  }
410  }
411  if (found == false)
412  names.push_back(pathJoin(dirName, fileName));
413  }
414  }
415  }
416  }
417  }
418 
419  void getDirs(FsEntry *restrict const entry,
420  std::string dirName,
421  StringVect &names)
422  {
423  ZipEntry *const zipEntry = static_cast<ZipEntry*>(entry);
424  const std::string subDir = zipEntry->subDir;
425  if (!subDir.empty())
426  dirName = pathJoin(subDir, dirName);
427  if (dirName == dirSeparator)
428  {
429  FOR_EACH (STD_VECTOR<ZipLocalHeader*>::const_iterator,
430  it2,
431  zipEntry->mHeaders)
432  {
433  ZipLocalHeader *const header = *it2;
434  std::string fileName = header->fileName;
435  // skip subdirs from enumeration
436  const size_t idx = fileName.find(dirSeparator);
437  if (idx != std::string::npos)
438  fileName.erase(idx);
439  bool found(false);
440  FOR_EACH (StringVectCIter, itn, names)
441  {
442  if (*itn == fileName)
443  {
444  found = true;
445  break;
446  }
447  }
448  if (found == false)
449  {
450  std::string dirName2 = pathJoin(dirName, fileName);
451  if (findLast(dirName2, std::string(dirSeparator)) == false)
452  dirName2 += dirSeparator;
453  FOR_EACH (STD_VECTOR<std::string>::const_iterator,
454  it,
455  zipEntry->mDirs)
456  {
457  if (*it == dirName2)
458  {
459  found = true;
460  break;
461  }
462  }
463  if (found == true)
464  names.push_back(fileName);
465  }
466  }
467  }
468  else
469  {
470  FOR_EACH (STD_VECTOR<ZipLocalHeader*>::const_iterator,
471  it2,
472  zipEntry->mHeaders)
473  {
474  ZipLocalHeader *const header = *it2;
475  std::string fileName = header->fileName;
476  if (findCutFirst(fileName, dirName) == true)
477  {
478  // skip subdirs from enumeration
479  const size_t idx = fileName.find(dirSeparator);
480  if (idx != std::string::npos)
481  fileName.erase(idx);
482  bool found(false);
483  FOR_EACH (StringVectCIter, itn, names)
484  {
485  if (*itn == fileName)
486  {
487  found = true;
488  break;
489  }
490  }
491  if (found == false)
492  {
493  std::string dirName2 = pathJoin(dirName, fileName);
494  if (findLast(dirName2, std::string(dirSeparator)) ==
495  false)
496  {
497  dirName2 += dirSeparator;
498  }
499  FOR_EACH (STD_VECTOR<std::string>::const_iterator,
500  it,
501  zipEntry->mDirs)
502  {
503  if (*it == dirName2)
504  {
505  found = true;
506  break;
507  }
508  }
509  if (found == true)
510  names.push_back(fileName);
511  }
512  }
513  }
514  }
515  }
516 
517  bool isDirectory(FsEntry *restrict const entry,
518  std::string dirName,
519  bool &isDirFlag)
520  {
521  ZipEntry *const zipEntry = static_cast<ZipEntry*>(entry);
522  std::string subDir = zipEntry->subDir;
523  if (!subDir.empty())
524  dirName = pathJoin(subDir, dirName);
525  FOR_EACH (STD_VECTOR<std::string>::const_iterator,
526  it2,
527  zipEntry->mDirs)
528  {
529  if (*it2 == dirName)
530  {
531  isDirFlag = true;
532  return true;
533  }
534  }
535  return false;
536  }
537 
538  void freeList(List *restrict const handle)
539  {
540  delete handle;
541  }
542 
543  File *openRead(FsEntry *restrict const entry,
544  std::string filename)
545  {
546  ZipEntry *const zipEntry = static_cast<ZipEntry*>(entry);
547  std::string subDir = zipEntry->subDir;
548  if (!subDir.empty())
549  filename = pathJoin(subDir, filename);
550  FOR_EACH (STD_VECTOR<ZipLocalHeader*>::const_iterator,
551  it2,
552  zipEntry->mHeaders)
553  {
554  const ZipLocalHeader *restrict const header = *it2;
555  if (header->fileName == filename)
556  {
557  const uint8_t *restrict const buf =
558  ZipReader::readFile(header);
559  if (buf == nullptr)
560  return nullptr;
561  return new File(&funcs,
562  buf,
563  header->uncompressSize);
564  }
565  }
566  return nullptr;
567  }
568 
570  const std::string &filename A_UNUSED)
571  {
572  reportAlways("VirtFs::openWrite for zip not implemented.")
573  return nullptr;
574  }
575 
577  const std::string &filename A_UNUSED)
578  {
579  reportAlways("VirtFs::openAppend for zip not implemented.")
580  return nullptr;
581  }
582 
583  int close(File *restrict const file)
584  {
585  if (file == nullptr)
586  return 0;
587  delete file;
588  return 1;
589  }
590 
591  int64_t read(File *restrict const file,
592  void *restrict const buffer,
593  const uint32_t objSize,
594  const uint32_t objCount)
595  {
596  if (file == nullptr ||
597  objSize == 0 ||
598  objCount == 0)
599  {
600  return 0;
601  }
602  if (buffer == nullptr)
603  {
604  reportAlways("FsZip::read buffer is null")
605  return 0;
606  }
607  const size_t pos = file->mPos;
608  const size_t sz = file->mSize;
609  // if outside of buffer, return
610  if (pos >= sz)
611  return 0;
612  // pointer to start for buffer ready to read
613  const uint8_t *restrict const memPtr = file->mBuf + pos;
614  // left buffer size from pos to end
615  const uint32_t memSize = CAST_U32(sz - pos);
616  // number of objects possible to read
617  uint32_t memCount = memSize / objSize;
618  if (memCount == 0)
619  return 0;
620  // limit number of possible objects to read to objCount
621  if (memCount > objCount)
622  memCount = objCount;
623  // number of bytes to read from buffer
624  const size_t memEnd = memCount * objSize;
625  memcpy(buffer, memPtr, memEnd);
626  file->mPos += memEnd;
627  return memCount;
628  }
629 
630  int64_t write(File *restrict const file A_UNUSED,
631  const void *restrict const buffer A_UNUSED,
632  const uint32_t objSize A_UNUSED,
633  const uint32_t objCount A_UNUSED)
634  {
635  return 0;
636  }
637 
638  int64_t fileLength(File *restrict const file)
639  {
640  if (file == nullptr)
641  return -1;
642 
643  return file->mSize;
644  }
645 
646  int64_t tell(File *restrict const file)
647  {
648  if (file == nullptr)
649  return -1;
650 
651  return file->mPos;
652  }
653 
654  int seek(File *restrict const file,
655  const uint64_t pos)
656  {
657  if (file == nullptr)
658  return 0;
659 
660  if (pos > file->mSize)
661  return 0;
662  file->mPos = pos;
663  return 1;
664  }
665 
666  int eof(File *restrict const file)
667  {
668  if (file == nullptr)
669  return -1;
670 
671  return static_cast<int>(file->mPos >= file->mSize);
672  }
673 
674  const char *loadFile(FsEntry *restrict const entry,
675  std::string filename,
676  int &restrict fileSize)
677  {
678  ZipEntry *const zipEntry = static_cast<ZipEntry*>(entry);
679  const std::string subDir = zipEntry->subDir;
680  if (!subDir.empty())
681  filename = pathJoin(subDir, filename);
682  FOR_EACH (STD_VECTOR<ZipLocalHeader*>::const_iterator,
683  it2,
684  zipEntry->mHeaders)
685  {
686  const ZipLocalHeader *restrict const header = *it2;
687  if (header->fileName == filename)
688  {
689  const uint8_t *restrict const buf =
690  ZipReader::readFile(header);
691  if (buf == nullptr)
692  return nullptr;
693 
694  logger->log("Loaded %s/%s",
695  entry->root.c_str(),
696  filename.c_str());
697 
698  fileSize = header->uncompressSize;
699  return reinterpret_cast<const char*>(buf);
700  }
701  }
702  return nullptr;
703  }
704 } // namespace FsZip
705 
706 } // namespace VirtFs
#define CAST_U32
Definition: cast.h:31
#define reportAlways(...)
Definition: checkutils.h:253
void log(const char *const log_text,...)
Definition: logger.cpp:269
#define FOR_EACH(type, iter, array)
Definition: foreach.h:25
const char * dirSeparator
Definition: fs.cpp:43
#define restrict
Definition: localconsts.h:165
#define A_UNUSED
Definition: localconsts.h:160
Logger * logger
Definition: logger.cpp:89
File * openWrite(FsEntry *const entry, const std::string &filename)
Definition: fszip.cpp:569
int rwops_read(SDL_RWops *const rw, void *const ptr, const int size, const int maxnum)
Definition: fsziprwops.cpp:112
void deinit()
Definition: fszip.cpp:56
bool isDirectory(FsEntry *const entry, std::string dirName, bool &isDirFlag)
Definition: fszip.cpp:517
void getDirs(FsEntry *const entry, std::string dirName, StringVect &names)
Definition: fszip.cpp:419
void enumerate(FsEntry *const entry, std::string dirName, StringVect &names)
Definition: fszip.cpp:157
void getFiles(FsEntry *const entry, std::string dirName, StringVect &names)
Definition: fszip.cpp:220
const char * loadFile(FsEntry *const entry, std::string filename, int &fileSize)
Definition: fszip.cpp:674
int rwops_write(SDL_RWops *const rw, const void *const ptr, const int size, const int num)
Definition: fsziprwops.cpp:129
bool getRealDir(FsEntry *const entry, std::string filename, std::string dirName, std::string &realDir)
Definition: fszip.cpp:94
int seek(File *const file, const uint64_t pos)
Definition: fszip.cpp:654
File * openAppend(FsEntry *const entry, const std::string &filename)
Definition: fszip.cpp:576
int64_t tell(File *const file)
Definition: fszip.cpp:646
void getFilesWithDir(FsEntry *const entry, const std::string &dirName, StringVect &names)
Definition: fszip.cpp:318
bool exists(FsEntry *const entry, std::string filename, std::string dirName)
Definition: fszip.cpp:129
int64_t read(File *const file, void *const buffer, const uint32_t objSize, const uint32_t objCount)
Definition: fszip.cpp:591
int64_t write(File *const file, const void *const buffer, const uint32_t objSize, const uint32_t objCount)
Definition: fszip.cpp:630
void initFuncs(FsFuncs *const ptr)
Definition: fszip.cpp:65
int eof(File *const file)
Definition: fszip.cpp:666
int rwops_close(SDL_RWops *const rw)
Definition: fsziprwops.cpp:137
void freeList(List *const handle)
Definition: fszip.cpp:538
int32_t rwops_seek(SDL_RWops *const rw, const int32_t offset, const int whence)
Definition: fsziprwops.cpp:43
int64_t fileLength(File *const file)
Definition: fszip.cpp:638
FsFuncs * getFuncs()
Definition: fszip.cpp:51
void init()
Definition: fszip.cpp:60
File * openRead(FsEntry *const entry, std::string filename)
Definition: fszip.cpp:543
int close(File *const file)
Definition: fszip.cpp:583
uint8_t * readFile(ZipLocalHeader *header)
Definition: zipreader.cpp:276
VirtFs::FsFuncs funcs
Definition: fszip.cpp:43
bool findCutFirst(std::string &str1, const std::string &str2)
bool findLast(const std::string &str1, const std::string &str2)
std::string pathJoin(std::string str1, const std::string &str2)
StringVect::const_iterator StringVectCIter
Definition: stringvector.h:31
std::vector< std::string > StringVect
Definition: stringvector.h:29
std::string subDir
Definition: fsentry.h:47
std::vector< std::string > mDirs
Definition: zipentry.h:47
std::vector< ZipLocalHeader * > mHeaders
Definition: zipentry.h:46
std::string fileName
Definition: testmain.cpp:39