GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/fs/virtfs/fszip.cpp Lines: 244 288 84.7 %
Date: 2018-11-12 Branches: 201 338 59.5 %

Line Branch Exec Source
1
/*
2
 *  The ManaPlus Client
3
 *  Copyright (C) 2013-2018  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
103
    FsFuncs *getFuncs()
51
    {
52
103
        return &funcs;
53
    }
54
55
119
    void deinit()
56
    {
57
119
    }
58
59
120
    void init()
60
    {
61
120
        initFuncs(&funcs);
62
120
    }
63
64
    void initFuncs(FsFuncs *restrict const ptr)
65
    {
66
120
        ptr->close = &FsZip::close;
67
120
        ptr->read = &FsZip::read;
68
120
        ptr->write = &FsZip::write;
69
120
        ptr->fileLength = &FsZip::fileLength;
70
120
        ptr->tell = &FsZip::tell;
71
120
        ptr->seek = &FsZip::seek;
72
120
        ptr->eof = &FsZip::eof;
73
120
        ptr->exists = &FsZip::exists;
74
120
        ptr->getRealDir = &FsZip::getRealDir;
75
120
        ptr->enumerate = &FsZip::enumerate;
76
120
        ptr->isDirectory = &FsZip::isDirectory;
77
120
        ptr->openRead = &FsZip::openRead;
78
120
        ptr->openWrite = &FsZip::openWrite;
79
120
        ptr->openAppend = &FsZip::openAppend;
80
120
        ptr->loadFile = &FsZip::loadFile;
81
120
        ptr->getFiles = &FsZip::getFiles;
82
120
        ptr->getFilesWithDir = &FsZip::getFilesWithDir;
83
120
        ptr->getDirs = &FsZip::getDirs;
84
120
        ptr->rwops_seek = &FsZip::rwops_seek;
85
120
        ptr->rwops_read = &FsZip::rwops_read;
86
120
        ptr->rwops_write = &FsZip::rwops_write;
87
120
        ptr->rwops_close = &FsZip::rwops_close;
88
#ifdef USE_SDL2
89
        ptr->rwops_size = &FsZip::rwops_size;
90
#endif  // USE_SDL2
91
    }
92
93
27
    bool getRealDir(FsEntry *restrict const entry,
94
                    std::string filename,
95
                    std::string dirName,
96
                    std::string &realDir)
97
    {
98
27
        ZipEntry *const zipEntry = static_cast<ZipEntry*>(entry);
99
81
        std::string subDir = zipEntry->subDir;
100
27
        if (!subDir.empty())
101
        {
102
6
            filename = pathJoin(subDir, filename);
103
6
            dirName = pathJoin(subDir, dirName);
104
        }
105
319
        FOR_EACH (STD_VECTOR<ZipLocalHeader*>::const_iterator,
106
                  it2,
107
                  zipEntry->mHeaders)
108
        {
109
169
            if ((*it2)->fileName == filename)
110
            {
111
12
                realDir = entry->root;
112
                return true;
113
            }
114
        }
115
130
        FOR_EACH (STD_VECTOR<std::string>::const_iterator,
116
                  it2,
117
                  zipEntry->mDirs)
118
        {
119
41
            if (*it2 == dirName)
120
            {
121
1
                realDir = entry->root;
122
                return true;
123
            }
124
        }
125
        return false;
126
    }
127
128
52
    bool exists(FsEntry *restrict const entry,
129
                std::string filename,
130
                std::string dirName)
131
    {
132
52
        ZipEntry *const zipEntry = static_cast<ZipEntry*>(entry);
133
156
        std::string subDir = zipEntry->subDir;
134
52
        if (!subDir.empty())
135
        {
136
39
            filename = pathJoin(subDir, filename);
137
39
            dirName = pathJoin(subDir, dirName);
138
        }
139
663
        FOR_EACH (STD_VECTOR<ZipLocalHeader*>::const_iterator,
140
                  it2,
141
                  zipEntry->mHeaders)
142
        {
143
365
            if ((*it2)->fileName == filename)
144
                return true;
145
        }
146
336
        FOR_EACH (STD_VECTOR<std::string>::const_iterator,
147
                  it2,
148
                  zipEntry->mDirs)
149
        {
150
112
            if (*it2 == dirName)
151
                return true;
152
        }
153
        return false;
154
    }
155
156
12
    void enumerate(FsEntry *restrict const entry,
157
                   std::string dirName,
158
                   StringVect &names)
159
    {
160
12
        ZipEntry *const zipEntry = static_cast<ZipEntry*>(entry);
161
36
        const std::string subDir = zipEntry->subDir;
162
12
        if (!subDir.empty())
163
6
            dirName = pathJoin(subDir, dirName);
164
24
        if (dirName == dirSeparator)
165
        {
166
50
            FOR_EACH (STD_VECTOR<ZipLocalHeader*>::const_iterator,
167
                      it2,
168
                      zipEntry->mHeaders)
169
            {
170
26
                ZipLocalHeader *const header = *it2;
171
78
                std::string fileName = header->fileName;
172
                // skip subdirs from enumeration
173
52
                const size_t idx = fileName.find(dirSeparator);
174
26
                if (idx != std::string::npos)
175
22
                    fileName.erase(idx);
176
26
                bool found(false);
177
164
                FOR_EACH (StringVectCIter, itn, names)
178
                {
179
50
                    if (*itn == fileName)
180
                    {
181
                        found = true;
182
                        break;
183
                    }
184
                }
185
26
                if (found == false)
186
10
                    names.push_back(fileName);
187
            }
188
        }
189
        else
190
        {
191
109
            FOR_EACH (STD_VECTOR<ZipLocalHeader*>::const_iterator,
192
                      it2,
193
                      zipEntry->mHeaders)
194
            {
195
61
                ZipLocalHeader *const header = *it2;
196
183
                std::string fileName = header->fileName;
197

61
                if (findCutFirst(fileName, dirName) == true)
198
                {
199
                    // skip subdirs from enumeration
200
52
                    const size_t idx = fileName.find(dirSeparator);
201
26
                    if (idx != std::string::npos)
202
6
                        fileName.erase(idx);
203
26
                    bool found(false);
204
212
                    FOR_EACH (StringVectCIter, itn, names)
205
                    {
206
85
                        if (*itn == fileName)
207
                        {
208
                            found = true;
209
                            break;
210
                        }
211
                    }
212
26
                    if (found == false)
213
23
                        names.push_back(fileName);
214
                }
215
            }
216
        }
217
12
    }
218
219
4
    void getFiles(FsEntry *restrict const entry,
220
                  std::string dirName,
221
                  StringVect &names)
222
    {
223
4
        ZipEntry *const zipEntry = static_cast<ZipEntry*>(entry);
224
12
        const std::string subDir = zipEntry->subDir;
225
4
        if (!subDir.empty())
226
6
            dirName = pathJoin(subDir, dirName);
227
8
        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
68
            FOR_EACH (STD_VECTOR<ZipLocalHeader*>::const_iterator,
271
                      it2,
272
                      zipEntry->mHeaders)
273
            {
274
44
                ZipLocalHeader *const header = *it2;
275
132
                std::string fileName = header->fileName;
276

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

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

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

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

64
                        if (findLast(dirName2, std::string(dirSeparator)) ==
396
                            false)
397
                        {
398
16
                            dirName2 += dirSeparator;
399
                        }
400
150
                        FOR_EACH (STD_VECTOR<std::string>::const_iterator,
401
                                  it,
402
                                  zipEntry->mDirs)
403
                        {
404
60
                            if (*it == dirName2)
405
                            {
406
                                found = true;
407
                                break;
408
                            }
409
                        }
410
16
                        if (found == false)
411
40
                            names.push_back(pathJoin(dirName, fileName));
412
                    }
413
                }
414
            }
415
        }
416
5
    }
417
418
4
    void getDirs(FsEntry *restrict const entry,
419
                 std::string dirName,
420
                 StringVect &names)
421
    {
422
4
        ZipEntry *const zipEntry = static_cast<ZipEntry*>(entry);
423
12
        const std::string subDir = zipEntry->subDir;
424
4
        if (!subDir.empty())
425
6
            dirName = pathJoin(subDir, dirName);
426
8
        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
68
            FOR_EACH (STD_VECTOR<ZipLocalHeader*>::const_iterator,
470
                      it2,
471
                      zipEntry->mHeaders)
472
            {
473
44
                ZipLocalHeader *const header = *it2;
474
132
                std::string fileName = header->fileName;
475

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

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

44
                    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
29
    int close(File *restrict const file)
583
    {
584
29
        if (file == nullptr)
585
            return 0;
586
29
        delete file;
587
29
        return 1;
588
    }
589
590
230
    int64_t read(File *restrict const file,
591
                 void *restrict const buffer,
592
                 const uint32_t objSize,
593
                 const uint32_t objCount)
594
    {
595
460
        if (file == nullptr ||
596
460
            objSize == 0 ||
597
            objCount == 0)
598
        {
599
            return 0;
600
        }
601
230
        if (buffer == nullptr)
602
        {
603
            reportAlways("FsZip::read buffer is null");
604
            return 0;
605
        }
606
230
        const size_t pos = file->mPos;
607
230
        const size_t sz = file->mSize;
608
        // if outside of buffer, return
609
230
        if (pos >= sz)
610
            return 0;
611
        // pointer to start for buffer ready to read
612
230
        const uint8_t *restrict const memPtr = file->mBuf + pos;
613
        // left buffer size from pos to end
614
230
        const uint32_t memSize = CAST_U32(sz - pos);
615
        // number of objects possible to read
616
230
        uint32_t memCount = memSize / objSize;
617
230
        if (memCount == 0)
618
            return 0;
619
        // limit number of possible objects to read to objCount
620
228
        if (memCount > objCount)
621
203
            memCount = objCount;
622
        // number of bytes to read from buffer
623
228
        const size_t memEnd = memCount * objSize;
624
228
        memcpy(buffer, memPtr, memEnd);
625
228
        file->mPos += memEnd;
626
228
        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
14
    int64_t fileLength(File *restrict const file)
638
    {
639
14
        if (file == nullptr)
640
            return -1;
641
642
14
        return file->mSize;
643
    }
644
645
78
    int64_t tell(File *restrict const file)
646
    {
647
78
        if (file == nullptr)
648
            return -1;
649
650
78
        return file->mPos;
651
    }
652
653
27
    int seek(File *restrict const file,
654
             const uint64_t pos)
655
    {
656
27
        if (file == nullptr)
657
            return 0;
658
659
27
        if (pos > file->mSize)
660
            return 0;
661
27
        file->mPos = pos;
662
27
        return 1;
663
    }
664
665
84
    int eof(File *restrict const file)
666
    {
667
84
        if (file == nullptr)
668
            return -1;
669
670
84
        return static_cast<int>(file->mPos >= file->mSize);
671
    }
672
673
4
    const char *loadFile(FsEntry *restrict const entry,
674
                         std::string filename,
675
                         int &restrict fileSize)
676
    {
677
4
        ZipEntry *const zipEntry = static_cast<ZipEntry*>(entry);
678
12
        const std::string subDir = zipEntry->subDir;
679
4
        if (!subDir.empty())
680
6
            filename = pathJoin(subDir, filename);
681
32
        FOR_EACH (STD_VECTOR<ZipLocalHeader*>::const_iterator,
682
                  it2,
683
                  zipEntry->mHeaders)
684
        {
685
12
            const ZipLocalHeader *restrict const header = *it2;
686
12
            if (header->fileName == filename)
687
            {
688
                const uint8_t *restrict const buf =
689
4
                    ZipReader::readFile(header);
690
4
                if (buf == nullptr)
691
                    return nullptr;
692
693
8
                logger->log("Loaded %s/%s",
694
                    entry->root.c_str(),
695
4
                    filename.c_str());
696
697
4
                fileSize = header->uncompressSize;
698
4
                return reinterpret_cast<const char*>(buf);
699
            }
700
        }
701
        return nullptr;
702
    }
703
}  // namespace FsZip
704
705

3
}  // namespace VirtFs