GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/fs/virtfs/fszip.cpp Lines: 243 287 84.7 %
Date: 2017-11-29 Branches: 171 296 57.8 %

Line Branch Exec Source
1
/*
2
 *  The ManaPlus Client
3
 *  Copyright (C) 2013-2017  The ManaPlus Developers
4
 *
5
 *  This file is part of The ManaPlus Client.
6
 *
7
 *  This program 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
 *  any later version.
11
 *
12
 *  This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
19
 */
20
21
#include "fs/virtfs/fszip.h"
22
23
#include "fs/virtfs/file.h"
24
#include "fs/virtfs/fsfuncs.h"
25
#include "fs/virtfs/fsziprwops.h"
26
#include "fs/virtfs/list.h"
27
#include "fs/virtfs/zipentry.h"
28
#include "fs/virtfs/zipreader.h"
29
#include "fs/virtfs/ziplocalheader.h"
30
31
#include "utils/cast.h"
32
#include "utils/checkutils.h"
33
#include "utils/foreach.h"
34
#include "utils/stringutils.h"
35
36
#include "debug.h"
37
38
extern const char *dirSeparator;
39
40
namespace
41
{
42
    VirtFs::FsFuncs funcs;
43
}  // namespace
44
45
namespace VirtFs
46
{
47
48
namespace FsZip
49
{
50
206
    FsFuncs *getFuncs()
51
    {
52
206
        return &funcs;
53
    }
54
55
238
    void deinit()
56
    {
57
238
    }
58
59
240
    void init()
60
    {
61
240
        initFuncs(&funcs);
62
240
    }
63
64
    void initFuncs(FsFuncs *restrict const ptr)
65
    {
66
240
        ptr->close = &FsZip::close;
67
240
        ptr->read = &FsZip::read;
68
240
        ptr->write = &FsZip::write;
69
240
        ptr->fileLength = &FsZip::fileLength;
70
240
        ptr->tell = &FsZip::tell;
71
240
        ptr->seek = &FsZip::seek;
72
240
        ptr->eof = &FsZip::eof;
73
240
        ptr->exists = &FsZip::exists;
74
240
        ptr->getRealDir = &FsZip::getRealDir;
75
240
        ptr->enumerate = &FsZip::enumerate;
76
240
        ptr->isDirectory = &FsZip::isDirectory;
77
240
        ptr->openRead = &FsZip::openRead;
78
240
        ptr->openWrite = &FsZip::openWrite;
79
240
        ptr->openAppend = &FsZip::openAppend;
80
240
        ptr->loadFile = &FsZip::loadFile;
81
240
        ptr->getFiles = &FsZip::getFiles;
82
240
        ptr->getFilesWithDir = &FsZip::getFilesWithDir;
83
240
        ptr->getDirs = &FsZip::getDirs;
84
240
        ptr->rwops_seek = &FsZip::rwops_seek;
85
240
        ptr->rwops_read = &FsZip::rwops_read;
86
240
        ptr->rwops_write = &FsZip::rwops_write;
87
240
        ptr->rwops_close = &FsZip::rwops_close;
88
#ifdef USE_SDL2
89
        ptr->rwops_size = &FsZip::rwops_size;
90
#endif  // USE_SDL2
91
    }
92
93
54
    bool getRealDir(FsEntry *restrict const entry,
94
                    std::string filename,
95
                    std::string dirName,
96
                    std::string &realDir)
97
    {
98
54
        ZipEntry *const zipEntry = static_cast<ZipEntry*>(entry);
99
162
        std::string subDir = zipEntry->subDir;
100
54
        if (!subDir.empty())
101
        {
102
12
            filename = pathJoin(subDir, filename);
103
12
            dirName = pathJoin(subDir, dirName);
104
        }
105
638
        FOR_EACH (STD_VECTOR<ZipLocalHeader*>::const_iterator,
106
                  it2,
107
                  zipEntry->mHeaders)
108
        {
109
362
            if ((*it2)->fileName == filename)
110
            {
111
24
                realDir = entry->root;
112
                return true;
113
            }
114
        }
115
260
        FOR_EACH (STD_VECTOR<std::string>::const_iterator,
116
                  it2,
117
                  zipEntry->mDirs)
118
        {
119
84
            if (*it2 == dirName)
120
            {
121
2
                realDir = entry->root;
122
                return true;
123
            }
124
        }
125
        return false;
126
    }
127
128
104
    bool exists(FsEntry *restrict const entry,
129
                std::string filename,
130
                std::string dirName)
131
    {
132
104
        ZipEntry *const zipEntry = static_cast<ZipEntry*>(entry);
133
312
        std::string subDir = zipEntry->subDir;
134
104
        if (!subDir.empty())
135
        {
136
78
            filename = pathJoin(subDir, filename);
137
78
            dirName = pathJoin(subDir, dirName);
138
        }
139
1326
        FOR_EACH (STD_VECTOR<ZipLocalHeader*>::const_iterator,
140
                  it2,
141
                  zipEntry->mHeaders)
142
        {
143
730
            if ((*it2)->fileName == filename)
144
                return true;
145
        }
146
672
        FOR_EACH (STD_VECTOR<std::string>::const_iterator,
147
                  it2,
148
                  zipEntry->mDirs)
149
        {
150
224
            if (*it2 == dirName)
151
                return true;
152
        }
153
        return false;
154
    }
155
156
24
    void enumerate(FsEntry *restrict const entry,
157
                   std::string dirName,
158
                   StringVect &names)
159
    {
160
24
        ZipEntry *const zipEntry = static_cast<ZipEntry*>(entry);
161
72
        const std::string subDir = zipEntry->subDir;
162
24
        if (!subDir.empty())
163
12
            dirName = pathJoin(subDir, dirName);
164
48
        if (dirName == dirSeparator)
165
        {
166
100
            FOR_EACH (STD_VECTOR<ZipLocalHeader*>::const_iterator,
167
                      it2,
168
                      zipEntry->mHeaders)
169
            {
170
52
                ZipLocalHeader *const header = *it2;
171
156
                std::string fileName = header->fileName;
172
                // skip subdirs from enumeration
173
104
                const size_t idx = fileName.find(dirSeparator);
174
52
                if (idx != std::string::npos)
175
44
                    fileName.erase(idx);
176
52
                bool found(false);
177
328
                FOR_EACH (StringVectCIter, itn, names)
178
                {
179
100
                    if (*itn == fileName)
180
                    {
181
                        found = true;
182
                        break;
183
                    }
184
                }
185
52
                if (found == false)
186
20
                    names.push_back(fileName);
187
            }
188
        }
189
        else
190
        {
191
218
            FOR_EACH (STD_VECTOR<ZipLocalHeader*>::const_iterator,
192
                      it2,
193
                      zipEntry->mHeaders)
194
            {
195
122
                ZipLocalHeader *const header = *it2;
196
366
                std::string fileName = header->fileName;
197

122
                if (findCutFirst(fileName, dirName) == true)
198
                {
199
                    // skip subdirs from enumeration
200
104
                    const size_t idx = fileName.find(dirSeparator);
201
52
                    if (idx != std::string::npos)
202
12
                        fileName.erase(idx);
203
52
                    bool found(false);
204
424
                    FOR_EACH (StringVectCIter, itn, names)
205
                    {
206
170
                        if (*itn == fileName)
207
                        {
208
                            found = true;
209
                            break;
210
                        }
211
                    }
212
52
                    if (found == false)
213
46
                        names.push_back(fileName);
214
                }
215
            }
216
        }
217
24
    }
218
219
8
    void getFiles(FsEntry *restrict const entry,
220
                  std::string dirName,
221
                  StringVect &names)
222
    {
223
8
        ZipEntry *const zipEntry = static_cast<ZipEntry*>(entry);
224
24
        const std::string subDir = zipEntry->subDir;
225
8
        if (!subDir.empty())
226
12
            dirName = pathJoin(subDir, dirName);
227
16
        if (dirName == dirSeparator)
228
        {
229
            FOR_EACH (STD_VECTOR<ZipLocalHeader*>::const_iterator,
230
                      it2,
231
                      zipEntry->mHeaders)
232
            {
233
                ZipLocalHeader *const header = *it2;
234
                std::string fileName = header->fileName;
235
                // skip subdirs from enumeration
236
                const size_t idx = fileName.find(dirSeparator);
237
                if (idx != std::string::npos)
238
                    fileName.erase(idx);
239
                bool found(false);
240
                FOR_EACH (StringVectCIter, itn, names)
241
                {
242
                    if (*itn == fileName)
243
                    {
244
                        found = true;
245
                        break;
246
                    }
247
                }
248
                if (found == false)
249
                {
250
                    std::string dirName2 = pathJoin(dirName, fileName);
251
                    if (findLast(dirName2, std::string(dirSeparator)) == false)
252
                        dirName2 += dirSeparator;
253
                    FOR_EACH (STD_VECTOR<std::string>::const_iterator,
254
                              it,
255
                              zipEntry->mDirs)
256
                    {
257
                        if (*it == dirName2)
258
                        {
259
                            found = true;
260
                            break;
261
                        }
262
                    }
263
                    if (found == false)
264
                        names.push_back(fileName);
265
                }
266
            }
267
        }
268
        else
269
        {
270
136
            FOR_EACH (STD_VECTOR<ZipLocalHeader*>::const_iterator,
271
                      it2,
272
                      zipEntry->mHeaders)
273
            {
274
88
                ZipLocalHeader *const header = *it2;
275
264
                std::string fileName = header->fileName;
276

88
                if (findCutFirst(fileName, dirName) == true)
277
                {
278
                    // skip subdirs from enumeration
279
64
                    const size_t idx = fileName.find(dirSeparator);
280
32
                    if (idx != std::string::npos)
281
12
                        fileName.erase(idx);
282
32
                    bool found(false);
283
190
                    FOR_EACH (StringVectCIter, itn, names)
284
                    {
285
30
                        if (*itn == fileName)
286
                        {
287
                            found = true;
288
                            break;
289
                        }
290
                    }
291
32
                    if (found == false)
292
                    {
293
96
                        std::string dirName2 = pathJoin(dirName, fileName);
294

128
                        if (findLast(dirName2, std::string(dirSeparator)) ==
295
                            false)
296
                        {
297
32
                            dirName2 += dirSeparator;
298
                        }
299
300
                        FOR_EACH (STD_VECTOR<std::string>::const_iterator,
300
                                  it,
301
                                  zipEntry->mDirs)
302
                        {
303
120
                            if (*it == dirName2)
304
                            {
305
                                found = true;
306
                                break;
307
                            }
308
                        }
309
32
                        if (found == false)
310
20
                            names.push_back(fileName);
311
                    }
312
                }
313
            }
314
        }
315
8
    }
316
317
10
    void getFilesWithDir(FsEntry *restrict const entry,
318
                         const std::string &dirName,
319
                         StringVect &names)
320
    {
321
10
        ZipEntry *const zipEntry = static_cast<ZipEntry*>(entry);
322
30
        const std::string subDir = zipEntry->subDir;
323
20
        std::string dirNameFull;
324
10
        if (!subDir.empty())
325
12
            dirNameFull = pathJoin(subDir, dirName);
326
        else
327
            dirNameFull = dirName;
328
20
        if (dirNameFull == dirSeparator)
329
        {
330
34
            FOR_EACH (STD_VECTOR<ZipLocalHeader*>::const_iterator,
331
                      it2,
332
                      zipEntry->mHeaders)
333
            {
334
22
                ZipLocalHeader *const header = *it2;
335
66
                std::string fileName = header->fileName;
336
                // skip subdirs from enumeration
337
44
                const size_t idx = fileName.find(dirSeparator);
338
22
                if (idx != std::string::npos)
339
18
                    fileName.erase(idx);
340
22
                bool found(false);
341
220
                FOR_EACH (StringVectCIter, itn, names)
342
                {
343
110
                    if (*itn == fileName)
344
                    {
345
                        found = true;
346
                        break;
347
                    }
348
                }
349
22
                if (found == false)
350
                {
351
66
                    std::string dirName2 = pathJoin(dirNameFull, fileName);
352

88
                    if (findLast(dirName2, std::string(dirSeparator)) == false)
353
22
                        dirName2 += dirSeparator;
354
220
                    FOR_EACH (STD_VECTOR<std::string>::const_iterator,
355
                              it,
356
                              zipEntry->mDirs)
357
                    {
358
88
                        if (*it == dirName2)
359
                        {
360
                            found = true;
361
                            break;
362
                        }
363
                    }
364
22
                    if (found == false)
365
88
                        names.push_back(pathJoin(dirName, fileName));
366
                }
367
            }
368
        }
369
        else
370
        {
371
136
            FOR_EACH (STD_VECTOR<ZipLocalHeader*>::const_iterator,
372
                      it2,
373
                      zipEntry->mHeaders)
374
            {
375
88
                ZipLocalHeader *const header = *it2;
376
264
                std::string fileName = header->fileName;
377

88
                if (findCutFirst(fileName, dirNameFull) == true)
378
                {
379
                    // skip subdirs from enumeration
380
64
                    const size_t idx = fileName.find(dirSeparator);
381
32
                    if (idx != std::string::npos)
382
12
                        fileName.erase(idx);
383
32
                    bool found(false);
384
190
                    FOR_EACH (StringVectCIter, itn, names)
385
                    {
386
30
                        if (*itn == fileName)
387
                        {
388
                            found = true;
389
                            break;
390
                        }
391
                    }
392
32
                    if (found == false)
393
                    {
394
96
                        std::string dirName2 = pathJoin(dirNameFull, fileName);
395

128
                        if (findLast(dirName2, std::string(dirSeparator)) ==
396
                            false)
397
                        {
398
32
                            dirName2 += dirSeparator;
399
                        }
400
300
                        FOR_EACH (STD_VECTOR<std::string>::const_iterator,
401
                                  it,
402
                                  zipEntry->mDirs)
403
                        {
404
120
                            if (*it == dirName2)
405
                            {
406
                                found = true;
407
                                break;
408
                            }
409
                        }
410
32
                        if (found == false)
411
80
                            names.push_back(pathJoin(dirName, fileName));
412
                    }
413
                }
414
            }
415
        }
416
10
    }
417
418
8
    void getDirs(FsEntry *restrict const entry,
419
                 std::string dirName,
420
                 StringVect &names)
421
    {
422
8
        ZipEntry *const zipEntry = static_cast<ZipEntry*>(entry);
423
24
        const std::string subDir = zipEntry->subDir;
424
8
        if (!subDir.empty())
425
12
            dirName = pathJoin(subDir, dirName);
426
16
        if (dirName == dirSeparator)
427
        {
428
            FOR_EACH (STD_VECTOR<ZipLocalHeader*>::const_iterator,
429
                      it2,
430
                      zipEntry->mHeaders)
431
            {
432
                ZipLocalHeader *const header = *it2;
433
                std::string fileName = header->fileName;
434
                // skip subdirs from enumeration
435
                const size_t idx = fileName.find(dirSeparator);
436
                if (idx != std::string::npos)
437
                    fileName.erase(idx);
438
                bool found(false);
439
                FOR_EACH (StringVectCIter, itn, names)
440
                {
441
                    if (*itn == fileName)
442
                    {
443
                        found = true;
444
                        break;
445
                    }
446
                }
447
                if (found == false)
448
                {
449
                    std::string dirName2 = pathJoin(dirName, fileName);
450
                    if (findLast(dirName2, std::string(dirSeparator)) == false)
451
                        dirName2 += dirSeparator;
452
                    FOR_EACH (STD_VECTOR<std::string>::const_iterator,
453
                              it,
454
                              zipEntry->mDirs)
455
                    {
456
                        if (*it == dirName2)
457
                        {
458
                            found = true;
459
                            break;
460
                        }
461
                    }
462
                    if (found == true)
463
                        names.push_back(fileName);
464
                }
465
            }
466
        }
467
        else
468
        {
469
136
            FOR_EACH (STD_VECTOR<ZipLocalHeader*>::const_iterator,
470
                      it2,
471
                      zipEntry->mHeaders)
472
            {
473
88
                ZipLocalHeader *const header = *it2;
474
264
                std::string fileName = header->fileName;
475

88
                if (findCutFirst(fileName, dirName) == true)
476
                {
477
                    // skip subdirs from enumeration
478
64
                    const size_t idx = fileName.find(dirSeparator);
479
32
                    if (idx != std::string::npos)
480
12
                        fileName.erase(idx);
481
32
                    bool found(false);
482
172
                    FOR_EACH (StringVectCIter, itn, names)
483
                    {
484
16
                        if (*itn == fileName)
485
                        {
486
                            found = true;
487
                            break;
488
                        }
489
                    }
490
32
                    if (found == false)
491
                    {
492
84
                        std::string dirName2 = pathJoin(dirName, fileName);
493

112
                        if (findLast(dirName2, std::string(dirSeparator)) ==
494
                            false)
495
                        {
496
28
                            dirName2 += dirSeparator;
497
                        }
498
268
                        FOR_EACH (STD_VECTOR<std::string>::const_iterator,
499
                                  it,
500
                                  zipEntry->mDirs)
501
                        {
502
108
                            if (*it == dirName2)
503
                            {
504
                                found = true;
505
                                break;
506
                            }
507
                        }
508
28
                        if (found == true)
509
8
                            names.push_back(fileName);
510
                    }
511
                }
512
            }
513
        }
514
8
    }
515
516
226
    bool isDirectory(FsEntry *restrict const entry,
517
                     std::string dirName,
518
                     bool &isDirFlag)
519
    {
520
226
        ZipEntry *const zipEntry = static_cast<ZipEntry*>(entry);
521
678
        std::string subDir = zipEntry->subDir;
522
226
        if (!subDir.empty())
523
102
            dirName = pathJoin(subDir, dirName);
524
1888
        FOR_EACH (STD_VECTOR<std::string>::const_iterator,
525
                  it2,
526
                  zipEntry->mDirs)
527
        {
528
632
            if (*it2 == dirName)
529
            {
530
50
                isDirFlag = true;
531
50
                return true;
532
            }
533
        }
534
        return false;
535
    }
536
537
    void freeList(List *restrict const handle)
538
    {
539
        delete handle;
540
    }
541
542
176
    File *openRead(FsEntry *restrict const entry,
543
                   std::string filename)
544
    {
545
176
        ZipEntry *const zipEntry = static_cast<ZipEntry*>(entry);
546
528
        std::string subDir = zipEntry->subDir;
547
176
        if (!subDir.empty())
548
162
            filename = pathJoin(subDir, filename);
549
2094
        FOR_EACH (STD_VECTOR<ZipLocalHeader*>::const_iterator,
550
                  it2,
551
                  zipEntry->mHeaders)
552
        {
553
1126
            const ZipLocalHeader *restrict const header = *it2;
554
1214
            if (header->fileName == filename)
555
            {
556
                const uint8_t *restrict const buf =
557
88
                    ZipReader::readFile(header);
558
88
                if (buf == nullptr)
559
                    return nullptr;
560
                return new File(&funcs,
561
                    buf,
562

88
                    header->uncompressSize);
563
            }
564
        }
565
        return nullptr;
566
    }
567
568
    File *openWrite(FsEntry *restrict const entry A_UNUSED,
569
                    const std::string &filename A_UNUSED)
570
    {
571
        reportAlways("VirtFs::openWrite for zip not implemented.");
572
        return nullptr;
573
    }
574
575
    File *openAppend(FsEntry *restrict const entry A_UNUSED,
576
                     const std::string &filename A_UNUSED)
577
    {
578
        reportAlways("VirtFs::openAppend for zip not implemented.");
579
        return nullptr;
580
    }
581
582
58
    int close(File *restrict const file)
583
    {
584
58
        if (file == nullptr)
585
            return 0;
586
58
        delete file;
587
58
        return 1;
588
    }
589
590
460
    int64_t read(File *restrict const file,
591
                 void *restrict const buffer,
592
                 const uint32_t objSize,
593
                 const uint32_t objCount)
594
    {
595
920
        if (file == nullptr ||
596
920
            objSize == 0 ||
597
            objCount == 0)
598
        {
599
            return 0;
600
        }
601
460
        if (buffer == nullptr)
602
        {
603
            reportAlways("FsZip::read buffer is null");
604
            return 0;
605
        }
606
460
        const size_t pos = file->mPos;
607
460
        const size_t sz = file->mSize;
608
        // if outside of buffer, return
609
460
        if (pos >= sz)
610
            return 0;
611
        // pointer to start for buffer ready to read
612
460
        const uint8_t *restrict const memPtr = file->mBuf + pos;
613
        // left buffer size from pos to end
614
460
        const uint32_t memSize = CAST_U32(sz - pos);
615
        // number of objects possible to read
616
460
        uint32_t memCount = memSize / objSize;
617
460
        if (memCount == 0)
618
            return 0;
619
        // limit number of possible objects to read to objCount
620
456
        if (memCount > objCount)
621
406
            memCount = objCount;
622
        // number of bytes to read from buffer
623
456
        const size_t memEnd = memCount * objSize;
624
456
        memcpy(buffer, memPtr, memEnd);
625
456
        file->mPos += memEnd;
626
456
        return memCount;
627
    }
628
629
    int64_t write(File *restrict const file A_UNUSED,
630
                  const void *restrict const buffer A_UNUSED,
631
                  const uint32_t objSize A_UNUSED,
632
                  const uint32_t objCount A_UNUSED)
633
    {
634
        return 0;
635
    }
636
637
28
    int64_t fileLength(File *restrict const file)
638
    {
639
28
        if (file == nullptr)
640
            return -1;
641
642
28
        return file->mSize;
643
    }
644
645
156
    int64_t tell(File *restrict const file)
646
    {
647
156
        if (file == nullptr)
648
            return -1;
649
650
156
        return file->mPos;
651
    }
652
653
54
    int seek(File *restrict const file,
654
             const uint64_t pos)
655
    {
656
54
        if (file == nullptr)
657
            return 0;
658
659
54
        if (pos > file->mSize)
660
            return 0;
661
54
        file->mPos = pos;
662
54
        return 1;
663
    }
664
665
168
    int eof(File *restrict const file)
666
    {
667
168
        if (file == nullptr)
668
            return -1;
669
670
168
        return static_cast<int>(file->mPos >= file->mSize);
671
    }
672
673
8
    const char *loadFile(FsEntry *restrict const entry,
674
                         std::string filename,
675
                         int &restrict fileSize)
676
    {
677
8
        ZipEntry *const zipEntry = static_cast<ZipEntry*>(entry);
678
24
        const std::string subDir = zipEntry->subDir;
679
8
        if (!subDir.empty())
680
12
            filename = pathJoin(subDir, filename);
681
64
        FOR_EACH (STD_VECTOR<ZipLocalHeader*>::const_iterator,
682
                  it2,
683
                  zipEntry->mHeaders)
684
        {
685
24
            const ZipLocalHeader *restrict const header = *it2;
686
32
            if (header->fileName == filename)
687
            {
688
                const uint8_t *restrict const buf =
689
8
                    ZipReader::readFile(header);
690
8
                if (buf == nullptr)
691
                    return nullptr;
692
693
24
                logger->log("Loaded %s/%s",
694
                    entry->root.c_str(),
695
                    filename.c_str());
696
697
8
                fileSize = header->uncompressSize;
698
8
                return reinterpret_cast<const char*>(buf);
699
            }
700
        }
701
        return nullptr;
702
    }
703
}  // namespace FsZip
704
705

6
}  // namespace VirtFs