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

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

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

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

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

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

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

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

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

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

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

3
}  // namespace VirtFs