Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/Common/File/VFS/SevenZipFileReader.cpp
10523 views
1
#include <algorithm>
2
#include <array>
3
#include <cstdio>
4
#include <cstdlib>
5
#include <cstring>
6
#include <set>
7
8
#ifndef _WIN32
9
#include <unistd.h>
10
#endif
11
12
#define NOMINMAX // 7z includes windows.h for some reason.
13
#include "ext/lzma-sdk/7z.h"
14
#include "ext/lzma-sdk/7zCrc.h"
15
#include "ext/lzma-sdk/7zFile.h"
16
#include "ext/lzma-sdk/Lzma2Dec.h"
17
#include "ext/lzma-sdk/LzmaDec.h"
18
19
#include "Common/Data/Encoding/Utf8.h"
20
#include "Common/Log.h"
21
#include "Common/StringUtils.h"
22
#include "Common/File/VFS/SevenZipFileReader.h"
23
24
static constexpr size_t SEVENZIP_LOOKBUF_SIZE = 1 << 14;
25
static constexpr size_t SEVENZIP_STREAM_LOOKAHEAD = 1 << 18;
26
static constexpr size_t SEVENZIP_SKIPBUF_SIZE = 1 << 15;
27
28
static constexpr UInt32 SEVENZIP_METHOD_COPY = 0;
29
static constexpr UInt32 SEVENZIP_METHOD_LZMA = 0x30101;
30
static constexpr UInt32 SEVENZIP_METHOD_LZMA2 = 0x21;
31
32
static WRes OpenArchiveStreamForRead(CFileInStream *stream, const Path &archivePath) {
33
#ifdef USE_WINDOWS_FILE
34
return InFile_OpenW(&stream->file, archivePath.ToWString().c_str());
35
#else
36
#if defined(HAVE_LIBRETRO_VFS)
37
#if PPSSPP_PLATFORM(ANDROID)
38
if (archivePath.Type() == PathType::CONTENT_URI) {
39
int fd = File::OpenFD(archivePath, File::OPEN_READ);
40
if (fd < 0) {
41
return 1;
42
}
43
stream->file.fd = fd;
44
return 0;
45
}
46
#endif
47
return InFile_Open(&stream->file, archivePath.ToString().c_str());
48
#else
49
FILE *file = File::OpenCFile(archivePath, "rb");
50
if (!file) {
51
return 1;
52
}
53
54
#if defined(USE_FOPEN)
55
stream->file.file = file;
56
return 0;
57
#else
58
const int openedFd = fileno(file);
59
if (openedFd < 0) {
60
fclose(file);
61
return 1;
62
}
63
64
const int fd = dup(openedFd);
65
fclose(file);
66
if (fd < 0) {
67
return 1;
68
}
69
70
stream->file.fd = fd;
71
return 0;
72
#endif
73
#endif
74
#endif
75
}
76
77
class SevenZipFileReference : public VFSFileReference {
78
public:
79
UInt32 index = 0;
80
};
81
82
class SevenZipOpenFile : public VFSOpenFile {
83
public:
84
enum class Mode {
85
MEMORY,
86
STREAM_COPY,
87
STREAM_LZMA,
88
STREAM_LZMA2,
89
};
90
91
SevenZipOpenFile() {
92
LzmaDec_CONSTRUCT(&lzma_);
93
Lzma2Dec_CONSTRUCT(&lzma2_);
94
archiveStream_.wres = 0;
95
File_Construct(&archiveStream_.file);
96
LookToRead2_INIT(&lookStream_);
97
}
98
99
~SevenZipOpenFile() override {
100
Release();
101
delete[] data;
102
}
103
104
void Release() {
105
if (lookStreamBuf_) {
106
free(lookStreamBuf_);
107
lookStreamBuf_ = nullptr;
108
}
109
if (lzmaAllocated_ && alloc_) {
110
LzmaDec_Free(&lzma_, alloc_);
111
lzmaAllocated_ = false;
112
}
113
if (lzma2Allocated_ && alloc_) {
114
Lzma2Dec_Free(&lzma2_, alloc_);
115
lzma2Allocated_ = false;
116
}
117
if (streamOpened_) {
118
File_Close(&archiveStream_.file);
119
streamOpened_ = false;
120
}
121
}
122
123
Mode mode = Mode::MEMORY;
124
125
uint8_t *data = nullptr;
126
size_t size = 0;
127
size_t offset = 0;
128
129
UInt32 fileIndex = 0;
130
UInt64 bytesToSkip = 0;
131
UInt64 fileRemaining = 0;
132
UInt64 packRemaining = 0;
133
UInt64 folderRemaining = 0;
134
bool streamError = false;
135
bool hasFileCRC = false;
136
UInt32 expectedFileCRC = 0;
137
UInt32 runningCRC = CRC_INIT_VAL;
138
bool crcChecked = false;
139
140
ISzAlloc *alloc_ = nullptr;
141
CFileInStream archiveStream_;
142
CLookToRead2 lookStream_;
143
Byte *lookStreamBuf_ = nullptr;
144
bool streamOpened_ = false;
145
CLzmaDec lzma_;
146
CLzma2Dec lzma2_;
147
bool lzmaAllocated_ = false;
148
bool lzma2Allocated_ = false;
149
std::array<uint8_t, SEVENZIP_SKIPBUF_SIZE> skipBuf_{};
150
};
151
152
static bool InitStreamingOpenFile(
153
const Path &archivePath,
154
const CSzArEx &db,
155
ISzAlloc *alloc,
156
UInt32 fileIndex,
157
SevenZipOpenFile *openFile) {
158
openFile->Release();
159
openFile->mode = SevenZipOpenFile::Mode::MEMORY;
160
openFile->alloc_ = alloc;
161
openFile->fileIndex = fileIndex;
162
163
const UInt32 folderIndex = db.FileToFolder[fileIndex];
164
if (folderIndex == (UInt32)-1) {
165
openFile->mode = SevenZipOpenFile::Mode::STREAM_COPY;
166
openFile->bytesToSkip = 0;
167
openFile->fileRemaining = 0;
168
openFile->folderRemaining = 0;
169
openFile->packRemaining = 0;
170
openFile->size = 0;
171
openFile->offset = 0;
172
return true;
173
}
174
175
const Byte *folderData = db.db.CodersData + db.db.FoCodersOffsets[folderIndex];
176
CSzData sd;
177
sd.Data = folderData;
178
sd.Size = db.db.FoCodersOffsets[(size_t)folderIndex + 1] - db.db.FoCodersOffsets[folderIndex];
179
CSzFolder folder;
180
if (SzGetNextFolderItem(&folder, &sd) != SZ_OK || sd.Size != 0) {
181
return false;
182
}
183
184
if (folder.NumCoders != 1 || folder.NumPackStreams != 1 || folder.NumBonds != 0 || folder.PackStreams[0] != 0) {
185
return false;
186
}
187
188
const CSzCoderInfo &coder = folder.Coders[0];
189
if (coder.NumStreams != 1) {
190
return false;
191
}
192
193
SevenZipOpenFile::Mode mode = SevenZipOpenFile::Mode::MEMORY;
194
if (coder.MethodID == SEVENZIP_METHOD_COPY) {
195
mode = SevenZipOpenFile::Mode::STREAM_COPY;
196
} else if (coder.MethodID == SEVENZIP_METHOD_LZMA) {
197
mode = SevenZipOpenFile::Mode::STREAM_LZMA;
198
} else if (coder.MethodID == SEVENZIP_METHOD_LZMA2) {
199
mode = SevenZipOpenFile::Mode::STREAM_LZMA2;
200
} else {
201
return false;
202
}
203
204
UInt64 packSize = 0;
205
const UInt64 *packPositions = db.db.PackPositions + db.db.FoStartPackStreamIndex[folderIndex];
206
packSize = packPositions[1] - packPositions[0];
207
208
const UInt64 unpackPos = db.UnpackPositions[fileIndex];
209
const UInt64 folderStartUnpackPos = db.UnpackPositions[db.FolderToFile[folderIndex]];
210
const UInt64 fileSize64 = db.UnpackPositions[(size_t)fileIndex + 1] - unpackPos;
211
const UInt64 folderSize64 = SzAr_GetFolderUnpackSize(&db.db, folderIndex);
212
const UInt64 fileOffsetInFolder = unpackPos - folderStartUnpackPos;
213
214
openFile->size = (size_t)fileSize64;
215
openFile->offset = 0;
216
openFile->mode = mode;
217
openFile->bytesToSkip = fileOffsetInFolder;
218
openFile->fileRemaining = fileSize64;
219
openFile->folderRemaining = folderSize64;
220
openFile->packRemaining = packSize;
221
openFile->streamError = false;
222
openFile->runningCRC = CRC_INIT_VAL;
223
openFile->crcChecked = false;
224
openFile->hasFileCRC = SzBitWithVals_Check(&db.CRCs, fileIndex);
225
openFile->expectedFileCRC = openFile->hasFileCRC ? db.CRCs.Vals[fileIndex] : 0;
226
227
if (OpenArchiveStreamForRead(&openFile->archiveStream_, archivePath) != 0) {
228
return false;
229
}
230
openFile->streamOpened_ = true;
231
232
openFile->lookStreamBuf_ = (Byte *)malloc(SEVENZIP_LOOKBUF_SIZE * sizeof(Byte));
233
if (!openFile->lookStreamBuf_) {
234
openFile->Release();
235
return false;
236
}
237
238
openFile->lookStream_.bufSize = SEVENZIP_LOOKBUF_SIZE;
239
openFile->lookStream_.buf = openFile->lookStreamBuf_;
240
openFile->lookStream_.realStream = &openFile->archiveStream_.vt;
241
FileInStream_CreateVTable(&openFile->archiveStream_);
242
LookToRead2_CreateVTable(&openFile->lookStream_, False);
243
LookToRead2_INIT(&openFile->lookStream_);
244
245
const UInt64 streamPos = db.dataPos + packPositions[0];
246
if (LookInStream_SeekTo(&openFile->lookStream_.vt, streamPos) != SZ_OK) {
247
openFile->Release();
248
return false;
249
}
250
251
const Byte *props = folderData + coder.PropsOffset;
252
if (mode == SevenZipOpenFile::Mode::STREAM_LZMA) {
253
if (LzmaDec_Allocate(&openFile->lzma_, props, coder.PropsSize, alloc) != SZ_OK) {
254
openFile->Release();
255
return false;
256
}
257
openFile->lzmaAllocated_ = true;
258
LzmaDec_Init(&openFile->lzma_);
259
} else if (mode == SevenZipOpenFile::Mode::STREAM_LZMA2) {
260
if (coder.PropsSize != 1 || Lzma2Dec_Allocate(&openFile->lzma2_, props[0], alloc) != SZ_OK) {
261
openFile->Release();
262
return false;
263
}
264
openFile->lzma2Allocated_ = true;
265
Lzma2Dec_Init(&openFile->lzma2_);
266
}
267
268
return true;
269
}
270
271
static size_t StreamCopyRead(SevenZipOpenFile *openFile, uint8_t *dest, size_t length) {
272
size_t produced = 0;
273
while (produced < length && openFile->packRemaining > 0) {
274
const size_t wanted = (size_t)std::min<UInt64>((UInt64)(length - produced), openFile->packRemaining);
275
size_t lookahead = wanted;
276
const void *inBuf = nullptr;
277
const SRes lookRes = ILookInStream_Look(&openFile->lookStream_.vt, &inBuf, &lookahead);
278
if (lookRes != SZ_OK || lookahead == 0) {
279
openFile->streamError = true;
280
break;
281
}
282
283
memcpy(dest + produced, inBuf, lookahead);
284
if (ILookInStream_Skip(&openFile->lookStream_.vt, lookahead) != SZ_OK) {
285
openFile->streamError = true;
286
break;
287
}
288
289
openFile->packRemaining -= lookahead;
290
produced += lookahead;
291
}
292
return produced;
293
}
294
295
static size_t StreamDecodeRead(SevenZipOpenFile *openFile, uint8_t *dest, size_t length) {
296
size_t produced = 0;
297
while (produced < length && openFile->packRemaining > 0) {
298
size_t lookahead = (size_t)std::min<UInt64>(SEVENZIP_STREAM_LOOKAHEAD, openFile->packRemaining);
299
const void *inBuf = nullptr;
300
const SRes lookRes = ILookInStream_Look(&openFile->lookStream_.vt, &inBuf, &lookahead);
301
if (lookRes != SZ_OK || lookahead == 0) {
302
openFile->streamError = true;
303
break;
304
}
305
306
SizeT inProcessed = (SizeT)lookahead;
307
SizeT outProcessed = (SizeT)(length - produced);
308
ELzmaStatus status = LZMA_STATUS_NOT_SPECIFIED;
309
SRes decodeRes = SZ_ERROR_UNSUPPORTED;
310
if (openFile->mode == SevenZipOpenFile::Mode::STREAM_LZMA) {
311
decodeRes = LzmaDec_DecodeToBuf(&openFile->lzma_, dest + produced, &outProcessed, (const Byte *)inBuf, &inProcessed, LZMA_FINISH_ANY, &status);
312
} else {
313
decodeRes = Lzma2Dec_DecodeToBuf(&openFile->lzma2_, dest + produced, &outProcessed, (const Byte *)inBuf, &inProcessed, LZMA_FINISH_ANY, &status);
314
}
315
316
if (decodeRes != SZ_OK) {
317
openFile->streamError = true;
318
break;
319
}
320
321
if (inProcessed > 0) {
322
if (ILookInStream_Skip(&openFile->lookStream_.vt, inProcessed) != SZ_OK) {
323
openFile->streamError = true;
324
break;
325
}
326
openFile->packRemaining -= inProcessed;
327
}
328
329
produced += outProcessed;
330
if (outProcessed > 0) {
331
if (openFile->folderRemaining < outProcessed) {
332
openFile->streamError = true;
333
break;
334
}
335
openFile->folderRemaining -= outProcessed;
336
}
337
338
if (inProcessed == 0 && outProcessed == 0) {
339
openFile->streamError = true;
340
break;
341
}
342
343
if (status == LZMA_STATUS_FINISHED_WITH_MARK || status == LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK) {
344
break;
345
}
346
}
347
return produced;
348
}
349
350
static size_t StreamReadChunk(SevenZipOpenFile *openFile, uint8_t *dest, size_t length) {
351
if (length == 0 || openFile->streamError) {
352
return 0;
353
}
354
if (openFile->mode == SevenZipOpenFile::Mode::STREAM_COPY) {
355
return StreamCopyRead(openFile, dest, length);
356
}
357
return StreamDecodeRead(openFile, dest, length);
358
}
359
360
void *SevenZipFileReader::Alloc(ISzAllocPtr, size_t size) {
361
return size == 0 ? nullptr : malloc(size);
362
}
363
364
void SevenZipFileReader::Free(ISzAllocPtr, void *address) {
365
free(address);
366
}
367
368
SevenZipFileReader::SevenZipFileReader(const Path &archivePath, const std::string &inArchivePath)
369
: archivePath_(archivePath), inArchivePath_(inArchivePath) {
370
if (!inArchivePath_.empty() && inArchivePath_.back() != '/') {
371
inArchivePath_.push_back('/');
372
}
373
374
archiveStream_.wres = 0;
375
File_Construct(&archiveStream_.file);
376
377
allocImp_.Alloc = Alloc;
378
allocImp_.Free = Free;
379
allocTempImp_.Alloc = Alloc;
380
allocTempImp_.Free = Free;
381
382
SzArEx_Init(&db_);
383
LookToRead2_INIT(&lookStream_);
384
}
385
386
SevenZipFileReader::~SevenZipFileReader() {
387
std::lock_guard<std::mutex> guard(lock_);
388
CloseArchive();
389
}
390
391
SevenZipFileReader *SevenZipFileReader::Create(const Path &archivePath, std::string_view inArchivePath, bool logErrors) {
392
SevenZipFileReader *reader = new SevenZipFileReader(archivePath, std::string(inArchivePath));
393
if (!reader->OpenArchive(logErrors)) {
394
delete reader;
395
return nullptr;
396
}
397
return reader;
398
}
399
400
bool SevenZipFileReader::OpenArchive(bool logErrors) {
401
std::lock_guard<std::mutex> guard(lock_);
402
403
if (valid_) {
404
return true;
405
}
406
407
static bool crcTableGenerated = false;
408
if (!crcTableGenerated) {
409
CrcGenerateTable();
410
crcTableGenerated = true;
411
}
412
413
if (OpenArchiveStreamForRead(&archiveStream_, archivePath_) != 0) {
414
if (logErrors) {
415
ERROR_LOG(Log::IO, "Failed to open %s as a 7z file", archivePath_.c_str());
416
}
417
return false;
418
}
419
420
lookStreamBuf_ = (Byte *)malloc(SEVENZIP_LOOKBUF_SIZE * sizeof(Byte));
421
if (!lookStreamBuf_) {
422
if (logErrors) {
423
ERROR_LOG(Log::IO, "Failed to allocate 7z look buffer");
424
}
425
File_Close(&archiveStream_.file);
426
return false;
427
}
428
429
lookStream_.bufSize = SEVENZIP_LOOKBUF_SIZE;
430
lookStream_.buf = lookStreamBuf_;
431
lookStream_.realStream = &archiveStream_.vt;
432
FileInStream_CreateVTable(&archiveStream_);
433
LookToRead2_CreateVTable(&lookStream_, False);
434
LookToRead2_INIT(&lookStream_);
435
436
const SRes res = SzArEx_Open(&db_, &lookStream_.vt, &allocImp_, &allocTempImp_);
437
if (res != SZ_OK) {
438
if (logErrors) {
439
ERROR_LOG(Log::IO, "Failed to parse %s as a 7z archive (error %d)", archivePath_.c_str(), res);
440
}
441
CloseArchive();
442
return false;
443
}
444
445
valid_ = BuildEntryCache();
446
if (!valid_ && logErrors) {
447
ERROR_LOG(Log::IO, "Failed to build file table for 7z archive %s", archivePath_.c_str());
448
}
449
return valid_;
450
}
451
452
void SevenZipFileReader::CloseArchive() {
453
if (cachedBlock_) {
454
IAlloc_Free(&allocImp_, cachedBlock_);
455
cachedBlock_ = nullptr;
456
cachedBlockSize_ = 0;
457
blockIndex_ = 0xFFFFFFFF;
458
}
459
460
entries_.clear();
461
SzArEx_Free(&db_, &allocImp_);
462
File_Close(&archiveStream_.file);
463
464
if (lookStreamBuf_) {
465
free(lookStreamBuf_);
466
lookStreamBuf_ = nullptr;
467
}
468
469
valid_ = false;
470
}
471
472
std::string SevenZipFileReader::ReadEntryPath(UInt32 index) const {
473
const size_t utf16Length = SzArEx_GetFileNameUtf16(&db_, index, nullptr);
474
if (utf16Length == 0) {
475
return std::string();
476
}
477
478
std::vector<UInt16> utf16(utf16Length);
479
SzArEx_GetFileNameUtf16(&db_, index, utf16.data());
480
481
size_t actualLen = utf16Length;
482
if (actualLen > 0 && utf16[actualLen - 1] == 0) {
483
--actualLen;
484
}
485
486
std::u16string utf16String;
487
utf16String.reserve(actualLen);
488
for (size_t i = 0; i < actualLen; ++i) {
489
utf16String.push_back((char16_t)utf16[i]);
490
}
491
492
std::string utf8 = ConvertUCS2ToUTF8(utf16String);
493
std::replace(utf8.begin(), utf8.end(), '\\', '/');
494
return utf8;
495
}
496
497
bool SevenZipFileReader::BuildEntryCache() {
498
entries_.clear();
499
entries_.reserve(db_.NumFiles);
500
501
for (UInt32 i = 0; i < db_.NumFiles; ++i) {
502
SevenZipEntry entry{};
503
entry.path = ReadEntryPath(i);
504
entry.isDirectory = SzArEx_IsDir(&db_, i) != 0;
505
entry.size = (uint64_t)SzArEx_GetFileSize(&db_, i);
506
entries_.push_back(std::move(entry));
507
}
508
509
return true;
510
}
511
512
std::string SevenZipFileReader::ResolvePath(std::string_view path) const {
513
std::string resolved = join(inArchivePath_, path);
514
std::replace(resolved.begin(), resolved.end(), '\\', '/');
515
return resolved;
516
}
517
518
bool SevenZipFileReader::FindEntry(std::string_view path, UInt32 *index, bool *isDirectory) const {
519
const std::string target = ResolvePath(path);
520
521
for (UInt32 i = 0; i < (UInt32)entries_.size(); ++i) {
522
const SevenZipEntry &entry = entries_[i];
523
if (equalsNoCase(entry.path, target)) {
524
*index = i;
525
if (isDirectory) {
526
*isDirectory = entry.isDirectory;
527
}
528
return true;
529
}
530
}
531
return false;
532
}
533
534
uint8_t *SevenZipFileReader::ExtractFile(UInt32 fileIndex, size_t *size) {
535
std::lock_guard<std::mutex> guard(lock_);
536
537
if (!valid_) {
538
return nullptr;
539
}
540
541
size_t offset = 0;
542
size_t outSizeProcessed = 0;
543
SRes res = SzArEx_Extract(
544
&db_,
545
&lookStream_.vt,
546
fileIndex,
547
&blockIndex_,
548
&cachedBlock_,
549
&cachedBlockSize_,
550
&offset,
551
&outSizeProcessed,
552
&allocImp_,
553
&allocTempImp_);
554
if (res != SZ_OK) {
555
ERROR_LOG(Log::IO, "Failed extracting '%s' from 7z archive (error %d)", entries_[fileIndex].path.c_str(), res);
556
return nullptr;
557
}
558
559
uint8_t *data = new uint8_t[outSizeProcessed + 1];
560
memcpy(data, cachedBlock_ + offset, outSizeProcessed);
561
data[outSizeProcessed] = 0;
562
*size = outSizeProcessed;
563
return data;
564
}
565
566
uint8_t *SevenZipFileReader::ReadFile(std::string_view path, size_t *size) {
567
UInt32 index = 0;
568
bool isDirectory = false;
569
if (!FindEntry(path, &index, &isDirectory) || isDirectory) {
570
return nullptr;
571
}
572
return ExtractFile(index, size);
573
}
574
575
VFSFileReference *SevenZipFileReader::GetFile(std::string_view path) {
576
UInt32 index = 0;
577
bool isDirectory = false;
578
if (!FindEntry(path, &index, &isDirectory) || isDirectory) {
579
return nullptr;
580
}
581
582
SevenZipFileReference *ref = new SevenZipFileReference();
583
ref->index = index;
584
return ref;
585
}
586
587
bool SevenZipFileReader::GetFileInfo(VFSFileReference *vfsReference, File::FileInfo *fileInfo) {
588
SevenZipFileReference *reference = (SevenZipFileReference *)vfsReference;
589
if (reference->index >= entries_.size()) {
590
return false;
591
}
592
593
const SevenZipEntry &entry = entries_[reference->index];
594
*fileInfo = File::FileInfo{};
595
fileInfo->isDirectory = entry.isDirectory;
596
fileInfo->isWritable = false;
597
fileInfo->exists = true;
598
fileInfo->size = entry.size;
599
return true;
600
}
601
602
void SevenZipFileReader::ReleaseFile(VFSFileReference *vfsReference) {
603
delete (SevenZipFileReference *)vfsReference;
604
}
605
606
VFSOpenFile *SevenZipFileReader::OpenFileForRead(VFSFileReference *vfsReference, size_t *size) {
607
SevenZipFileReference *reference = (SevenZipFileReference *)vfsReference;
608
if (reference->index >= entries_.size()) {
609
return nullptr;
610
}
611
if (entries_[reference->index].isDirectory) {
612
return nullptr;
613
}
614
615
SevenZipOpenFile *openFile = new SevenZipOpenFile();
616
if (!InitStreamingOpenFile(archivePath_, db_, &allocImp_, reference->index, openFile)) {
617
// Fallback for unsupported folder graphs (for example BCJ2).
618
openFile->mode = SevenZipOpenFile::Mode::MEMORY;
619
openFile->data = ExtractFile(reference->index, &openFile->size);
620
if (!openFile->data) {
621
delete openFile;
622
return nullptr;
623
}
624
}
625
626
openFile->offset = 0;
627
*size = openFile->size;
628
return openFile;
629
}
630
631
void SevenZipFileReader::Rewind(VFSOpenFile *vfsOpenFile) {
632
SevenZipOpenFile *openFile = (SevenZipOpenFile *)vfsOpenFile;
633
if (openFile->mode != SevenZipOpenFile::Mode::MEMORY) {
634
if (!InitStreamingOpenFile(archivePath_, db_, &allocImp_, openFile->fileIndex, openFile)) {
635
openFile->streamError = true;
636
}
637
return;
638
}
639
openFile->offset = 0;
640
}
641
642
size_t SevenZipFileReader::Read(VFSOpenFile *vfsOpenFile, void *buffer, size_t length) {
643
SevenZipOpenFile *openFile = (SevenZipOpenFile *)vfsOpenFile;
644
if (openFile->mode != SevenZipOpenFile::Mode::MEMORY) {
645
size_t produced = 0;
646
uint8_t *dest = (uint8_t *)buffer;
647
648
while (produced < length && openFile->fileRemaining > 0 && !openFile->streamError) {
649
if (openFile->bytesToSkip > 0) {
650
const size_t skipReq = (size_t)std::min<UInt64>(openFile->bytesToSkip, openFile->skipBuf_.size());
651
const size_t skipped = StreamReadChunk(openFile, openFile->skipBuf_.data(), skipReq);
652
if (skipped == 0) {
653
openFile->streamError = true;
654
break;
655
}
656
openFile->bytesToSkip -= skipped;
657
continue;
658
}
659
660
const size_t wanted = (size_t)std::min<UInt64>((UInt64)(length - produced), openFile->fileRemaining);
661
const size_t got = StreamReadChunk(openFile, dest + produced, wanted);
662
if (got == 0) {
663
openFile->streamError = true;
664
break;
665
}
666
667
if (openFile->hasFileCRC) {
668
openFile->runningCRC = CrcUpdate(openFile->runningCRC, dest + produced, got);
669
}
670
671
produced += got;
672
openFile->fileRemaining -= got;
673
openFile->offset += got;
674
}
675
676
if (openFile->fileRemaining == 0 && openFile->hasFileCRC && !openFile->crcChecked) {
677
openFile->crcChecked = true;
678
if (CRC_GET_DIGEST(openFile->runningCRC) != openFile->expectedFileCRC) {
679
openFile->streamError = true;
680
ERROR_LOG(Log::IO, "CRC mismatch while streaming '%s' from 7z archive", entries_[openFile->fileIndex].path.c_str());
681
}
682
}
683
684
return produced;
685
}
686
687
if (openFile->offset >= openFile->size) {
688
return 0;
689
}
690
691
const size_t remaining = openFile->size - openFile->offset;
692
const size_t toRead = std::min(length, remaining);
693
memcpy(buffer, openFile->data + openFile->offset, toRead);
694
openFile->offset += toRead;
695
return toRead;
696
}
697
698
void SevenZipFileReader::CloseFile(VFSOpenFile *vfsOpenFile) {
699
delete (SevenZipOpenFile *)vfsOpenFile;
700
}
701
702
bool SevenZipFileReader::GetFileListing(std::string_view origPath, std::vector<File::FileInfo> *listing, const char *filter) {
703
std::string path = ResolvePath(origPath);
704
if (!path.empty() && path.back() != '/') {
705
path.push_back('/');
706
}
707
708
std::set<std::string> filters;
709
std::string tmp;
710
if (filter) {
711
while (*filter) {
712
if (*filter == ':') {
713
filters.emplace("." + tmp);
714
tmp.clear();
715
} else {
716
tmp.push_back(*filter);
717
}
718
filter++;
719
}
720
}
721
if (!tmp.empty()) {
722
filters.emplace("." + tmp);
723
}
724
725
std::set<std::string> files;
726
std::set<std::string> directories;
727
for (const auto &entry : entries_) {
728
if (!startsWith(entry.path, path)) {
729
continue;
730
}
731
732
if (entry.path.size() == path.size()) {
733
continue;
734
}
735
736
std::string_view relative = std::string_view(entry.path).substr(path.size());
737
size_t slashPos = relative.find('/');
738
if (slashPos != std::string::npos) {
739
directories.emplace(std::string(relative.substr(0, slashPos)));
740
} else if (!entry.isDirectory) {
741
files.emplace(std::string(relative));
742
}
743
}
744
745
listing->clear();
746
747
const std::string relativePath = path.substr(inArchivePath_.size());
748
listing->reserve(directories.size() + files.size());
749
750
for (const auto &dir : directories) {
751
File::FileInfo info;
752
info.name = dir;
753
info.fullName = Path(relativePath + dir);
754
info.exists = true;
755
info.isWritable = false;
756
info.isDirectory = true;
757
listing->push_back(info);
758
}
759
760
for (const auto &file : files) {
761
File::FileInfo info;
762
info.name = file;
763
info.fullName = Path(relativePath + file);
764
info.exists = true;
765
info.isWritable = false;
766
info.isDirectory = false;
767
if (filter) {
768
std::string ext = info.fullName.GetFileExtension();
769
if (filters.find(ext) == filters.end()) {
770
continue;
771
}
772
}
773
listing->push_back(info);
774
}
775
776
std::sort(listing->begin(), listing->end());
777
return !listing->empty();
778
}
779
780
bool SevenZipFileReader::GetFileInfo(std::string_view path, File::FileInfo *info) {
781
*info = File::FileInfo{};
782
info->fullName = Path(path);
783
info->isWritable = false;
784
785
UInt32 index = 0;
786
bool isDirectory = false;
787
if (FindEntry(path, &index, &isDirectory)) {
788
const SevenZipEntry &entry = entries_[index];
789
info->exists = true;
790
info->isDirectory = entry.isDirectory;
791
info->size = entry.size;
792
return true;
793
}
794
795
const std::string base = ResolvePath(path);
796
const std::string prefix = base.empty() || base.back() == '/' ? base : base + "/";
797
for (const auto &entry : entries_) {
798
if (startsWith(entry.path, prefix)) {
799
info->exists = true;
800
info->isDirectory = true;
801
info->size = 0;
802
return true;
803
}
804
}
805
806
info->exists = false;
807
return false;
808
}
809
810