Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/Core/FileLoaders/ZipFileLoader.cpp
3186 views
1
#ifdef SHARED_LIBZIP
2
#include <zip.h>
3
#else
4
#include "ext/libzip/zip.h"
5
#endif
6
7
#include "Core/FileLoaders/LocalFileLoader.h"
8
#include "Core/FileLoaders/ZipFileLoader.h"
9
10
ZipFileLoader::ZipFileLoader(FileLoader *sourceLoader)
11
: ProxiedFileLoader(sourceLoader), zipArchive_(nullptr) {
12
if (!backend_ || !backend_->Exists() || backend_->IsDirectory()) {
13
return;
14
}
15
16
zip_error_t error{};
17
zip_source_t* zipSource = zip_source_function_create([](void* userdata, void* data, zip_uint64_t len, zip_source_cmd_t cmd) -> zip_int64_t {
18
ZipFileLoader *loader = (ZipFileLoader *)userdata;
19
return loader->ZipSourceCallback(data, len, cmd);
20
}, this, &error);
21
if (!zipSource) {
22
ERROR_LOG(Log::IO, "Failed to create ZIP source: %s", zip_error_strerror(&error));
23
return;
24
}
25
26
zipArchive_ = zip_open_from_source(zipSource, ZIP_RDONLY, &error);
27
if (!zipArchive_) {
28
ERROR_LOG(Log::IO, "Failed to open ZIP archive: %s", zip_error_strerror(&error));
29
zip_source_free(zipSource);
30
}
31
}
32
33
ZipFileLoader::~ZipFileLoader() {
34
if (dataFile_) {
35
zip_fclose(dataFile_);
36
}
37
if (zipArchive_) {
38
zip_discard(zipArchive_);
39
}
40
if (data_) {
41
free(data_);
42
}
43
}
44
45
bool ZipFileLoader::Initialize(int fileIndex) {
46
_dbg_assert_(!data_);
47
48
struct zip_stat zstat;
49
int retval = zip_stat_index(zipArchive_, fileIndex, ZIP_FL_NOCASE | ZIP_FL_UNCHANGED, &zstat);
50
if (retval < 0) {
51
return false;
52
}
53
const char *name = zip_get_name(zipArchive_, fileIndex, ZIP_FL_NOCASE | ZIP_FL_UNCHANGED);
54
fileExtension_ = KeepIncludingLast(name, '.');
55
56
_dbg_assert_(zstat.index == fileIndex);
57
dataFileSize_ = zstat.size;
58
dataFile_ = zip_fopen_index(zipArchive_, zstat.index, ZIP_FL_UNCHANGED);
59
data_ = (u8 *)malloc(dataFileSize_);
60
return data_ != nullptr;
61
}
62
63
size_t ZipFileLoader::ReadAt(s64 absolutePos, size_t bytes, void *data, Flags flags) {
64
if (!dataFile_ || absolutePos < 0 || absolutePos >= dataFileSize_) {
65
return 0;
66
}
67
68
if (absolutePos + (s64)bytes > dataFileSize_) {
69
// TODO: This could go negative..
70
bytes = dataFileSize_ - absolutePos;
71
}
72
73
// Decompress until the requested point, filling up data_ as we go. TODO: Do on thread.
74
while (dataReadPos_ < absolutePos + (s64)bytes) {
75
int remaining = BLOCK_SIZE;
76
if (dataReadPos_ + remaining > dataFileSize_) {
77
remaining = (int)(dataFileSize_ - dataReadPos_);
78
}
79
zip_int64_t retval = zip_fread(dataFile_, data_ + dataReadPos_, remaining);
80
_dbg_assert_(retval == remaining);
81
dataReadPos_ += retval;
82
}
83
84
// Perform the read.
85
memcpy(data, data_ + absolutePos, bytes);
86
return bytes;
87
}
88
89
zip_int64_t ZipFileLoader::ZipSourceCallback(void *data, zip_uint64_t len, zip_source_cmd_t cmd) {
90
switch (cmd) {
91
case ZIP_SOURCE_OPEN:
92
{
93
zipReadPos_ = 0;
94
return 0;
95
}
96
case ZIP_SOURCE_READ:
97
{
98
size_t readBytes = static_cast<zip_int64_t>(backend_->ReadAt(zipReadPos_, len, data));
99
zipReadPos_ += readBytes;
100
return readBytes;
101
}
102
case ZIP_SOURCE_SEEK:
103
{
104
struct SeekData {
105
zip_int64_t offset;
106
int whence;
107
};
108
if (len < sizeof(SeekData)) {
109
return -1; // Invalid argument size
110
}
111
const SeekData *seekData = static_cast<const SeekData*>(data);
112
zip_int64_t new_offset;
113
switch (seekData->whence) {
114
case SEEK_SET:
115
new_offset = seekData->offset;
116
break;
117
case SEEK_CUR:
118
new_offset = zipReadPos_ + seekData->offset;
119
break;
120
case SEEK_END:
121
new_offset = backend_->FileSize() + seekData->offset;
122
break;
123
default:
124
return -1; // Invalid 'whence' value
125
}
126
if (new_offset < 0 || new_offset > backend_->FileSize()) {
127
return -1; // Offset out of bounds
128
}
129
zipReadPos_ = new_offset;
130
return 0;
131
}
132
case ZIP_SOURCE_TELL:
133
return zipReadPos_;
134
case ZIP_SOURCE_CLOSE:
135
return 0;
136
case ZIP_SOURCE_STAT:
137
{
138
if (len < sizeof(zip_stat_t)) {
139
return -1;
140
}
141
zip_stat_t* st = static_cast<zip_stat_t*>(data);
142
zip_stat_init(st);
143
st->valid = ZIP_STAT_SIZE;
144
st->size = static_cast<zip_uint64_t>(backend_->FileSize());
145
return sizeof(zip_stat_t);
146
}
147
case ZIP_SOURCE_ERROR:
148
return -1;
149
case ZIP_SOURCE_FREE:
150
return 0;
151
case ZIP_SOURCE_SUPPORTS:
152
return zip_source_make_command_bitmap(ZIP_SOURCE_READ, ZIP_SOURCE_SEEK, ZIP_SOURCE_TELL, ZIP_SOURCE_OPEN, ZIP_SOURCE_CLOSE, ZIP_SOURCE_STAT);
153
default:
154
return -1;
155
}
156
}
157
158