all repos — mgba @ 18e67378fe24815ab76ed97fd5f1497e0a9d3e6a

mGBA Game Boy Advance Emulator

src/util/vfs/vfs-zip.c (view raw)

  1#include "util/vfs.h"
  2
  3#ifdef ENABLE_LIBZIP
  4#include <zip.h>
  5
  6
  7enum {
  8	BLOCK_SIZE = 1024
  9};
 10
 11struct VDirEntryZip {
 12	struct VDirEntry d;
 13	struct zip* z;
 14	zip_int64_t index;
 15};
 16
 17struct VDirZip {
 18	struct VDir d;
 19	struct zip* z;
 20	struct VDirEntryZip dirent;
 21};
 22
 23struct VFileZip {
 24	struct VFile d;
 25	struct zip_file* zf;
 26	void* buffer;
 27	size_t offset;
 28	size_t bufferSize;
 29	size_t readSize;
 30	size_t fileSize;
 31};
 32
 33static bool _vfzClose(struct VFile* vf);
 34static off_t _vfzSeek(struct VFile* vf, off_t offset, int whence);
 35static ssize_t _vfzRead(struct VFile* vf, void* buffer, size_t size);
 36static ssize_t _vfzReadline(struct VFile* vf, char* buffer, size_t size);
 37static ssize_t _vfzWrite(struct VFile* vf, void* buffer, size_t size);
 38static void* _vfzMap(struct VFile* vf, size_t size, int flags);
 39static void _vfzUnmap(struct VFile* vf, void* memory, size_t size);
 40static void _vfzTruncate(struct VFile* vf, size_t size);
 41
 42static bool _vdzClose(struct VDir* vd);
 43static void _vdzRewind(struct VDir* vd);
 44static struct VDirEntry* _vdzListNext(struct VDir* vd);
 45static struct VFile* _vdzOpenFile(struct VDir* vd, const char* path, int mode);
 46
 47static const char* _vdezName(struct VDirEntry* vde);
 48
 49struct VDir* VDirOpenZip(const char* path, int flags) {
 50	int zflags = 0;
 51	if (flags & O_CREAT) {
 52		zflags |= ZIP_CREATE;
 53	}
 54	if (flags & O_EXCL) {
 55		zflags |= ZIP_EXCL;
 56	}
 57
 58	struct zip* z = zip_open(path, zflags, 0);
 59	if (!z) {
 60		return 0;
 61	}
 62	struct VDirZip* vd = malloc(sizeof(struct VDirZip));
 63
 64	vd->d.close = _vdzClose;
 65	vd->d.rewind = _vdzRewind;
 66	vd->d.listNext = _vdzListNext;
 67	vd->d.openFile = _vdzOpenFile;
 68	vd->z = z;
 69
 70	vd->dirent.d.name = _vdezName;
 71	vd->dirent.index = -1;
 72	vd->dirent.z = z;
 73
 74	return &vd->d;
 75}
 76
 77bool _vfzClose(struct VFile* vf) {
 78	struct VFileZip* vfz = (struct VFileZip*) vf;
 79	if (zip_fclose(vfz->zf) < 0) {
 80		return false;
 81	}
 82	free(vfz->buffer);
 83	free(vfz);
 84	return true;
 85}
 86
 87off_t _vfzSeek(struct VFile* vf, off_t offset, int whence) {
 88	struct VFileZip* vfz = (struct VFileZip*) vf;
 89
 90	size_t position;
 91	switch (whence) {
 92	case SEEK_SET:
 93		position = offset;
 94		break;
 95	case SEEK_CUR:
 96		if (offset < 0 && ((vfz->offset < (size_t) -offset) || (offset == INT_MIN))) {
 97			return -1;
 98		}
 99		position = vfz->offset + offset;
100		break;
101	case SEEK_END:
102		if (offset < 0 && ((vfz->fileSize < (size_t) -offset) || (offset == INT_MIN))) {
103			return -1;
104		}
105		position = vfz->fileSize + offset;
106		break;
107	default:
108		return -1;
109	}
110
111	if (position <= vfz->offset) {
112		vfz->offset = position;
113		return position;
114	}
115
116	if (position <= vfz->fileSize) {
117		ssize_t read = vf->read(vf, 0, position - vfz->offset);
118		if (read < 0) {
119			return -1;
120		}
121		return vfz->offset;
122	}
123
124	return -1;
125}
126
127ssize_t _vfzRead(struct VFile* vf, void* buffer, size_t size) {
128	struct VFileZip* vfz = (struct VFileZip*) vf;
129
130	size_t bytesRead = 0;
131	if (!vfz->buffer) {
132		vfz->bufferSize = BLOCK_SIZE;
133		vfz->buffer = malloc(BLOCK_SIZE);
134	}
135
136	while (bytesRead < size) {
137		if (vfz->offset < vfz->readSize) {
138			size_t diff = vfz->readSize - vfz->offset;
139			void* start = &((uint8_t*) vfz->buffer)[vfz->offset];
140			if (diff > size - bytesRead) {
141				diff = size - bytesRead;
142			}
143			if (buffer) {
144				void* bufferOffset = &((uint8_t*) buffer)[bytesRead];
145				memcpy(bufferOffset, start, diff);
146			}
147			vfz->offset += diff;
148			bytesRead += diff;
149			if (diff == size) {
150				break;
151			}
152		}
153		// offset == readSize
154		if (vfz->readSize == vfz->bufferSize) {
155			vfz->bufferSize *= 2;
156			if (vfz->bufferSize > vfz->fileSize) {
157				vfz->bufferSize = vfz->fileSize;
158			}
159			vfz->buffer = realloc(vfz->buffer, vfz->bufferSize);
160		}
161		if (vfz->readSize < vfz->bufferSize) {
162			void* start = &((uint8_t*) vfz->buffer)[vfz->readSize];
163			size_t toRead = vfz->bufferSize - vfz->readSize;
164			if (toRead > BLOCK_SIZE) {
165				toRead = BLOCK_SIZE;
166			}
167			ssize_t zipRead = zip_fread(vfz->zf, start, toRead);
168			if (zipRead < 0) {
169				if (bytesRead == 0) {
170					return -1;
171				}
172				break;
173			}
174			if (zipRead == 0) {
175				break;
176			}
177			vfz->readSize += zipRead;
178		} else {
179			break;
180		}
181	}
182	return bytesRead;
183}
184
185ssize_t _vfzReadline(struct VFile* vf, char* buffer, size_t size) {
186	size_t bytesRead = 0;
187	while (bytesRead < size - 1) {
188		size_t newRead = vf->read(vf, &buffer[bytesRead], 1);
189		bytesRead += newRead;
190		if (!newRead || buffer[bytesRead] == '\n') {
191			break;
192		}
193	}
194	return buffer[bytesRead] = '\0';
195}
196
197ssize_t _vfzWrite(struct VFile* vf, void* buffer, size_t size) {
198	// TODO
199	UNUSED(vf);
200	UNUSED(buffer);
201	UNUSED(size);
202	return -1;
203}
204
205void* _vfzMap(struct VFile* vf, size_t size, int flags) {
206	struct VFileZip* vfz = (struct VFileZip*) vf;
207
208	UNUSED(flags);
209	if (size > vfz->readSize) {
210		vf->read(vf, 0, size - vfz->readSize);
211	}
212	return vfz->buffer;
213}
214
215void _vfzUnmap(struct VFile* vf, void* memory, size_t size) {
216	UNUSED(vf);
217	UNUSED(memory);
218	UNUSED(size);
219}
220
221void _vfzTruncate(struct VFile* vf, size_t size) {
222	// TODO
223	UNUSED(vf);
224	UNUSED(size);
225}
226
227bool _vdzClose(struct VDir* vd) {
228	struct VDirZip* vdz = (struct VDirZip*) vd;
229	if (zip_close(vdz->z) < 0) {
230		return false;
231	}
232	free(vdz);
233	return true;
234}
235
236void _vdzRewind(struct VDir* vd) {
237	struct VDirZip* vdz = (struct VDirZip*) vd;
238	vdz->dirent.index = -1;
239}
240
241struct VDirEntry* _vdzListNext(struct VDir* vd) {
242	struct VDirZip* vdz = (struct VDirZip*) vd;
243	zip_int64_t maxIndex = zip_get_num_entries(vdz->z, 0);
244	if (maxIndex <= vdz->dirent.index + 1) {
245		return 0;
246	}
247	++vdz->dirent.index;
248	return &vdz->dirent.d;
249}
250
251struct VFile* _vdzOpenFile(struct VDir* vd, const char* path, int mode) {
252	UNUSED(mode);
253	// TODO: support truncating, appending and creating, and write
254	struct VDirZip* vdz = (struct VDirZip*) vd;
255
256	if ((mode & O_RDWR) == O_RDWR) {
257		// libzip doesn't allow for random access, so read/write is impossible without
258		// reading the entire file first. This approach will be supported eventually.
259		return 0;
260	}
261
262	if (mode & O_WRONLY) {
263		// Write support is not yet implemented.
264		return 0;
265	}
266
267	struct zip_stat s;
268	if (zip_stat(vdz->z, path, 0, &s) < 0) {
269		return 0;
270	}
271
272	struct zip_file* zf = zip_fopen(vdz->z, path, 0);
273	if (!zf) {
274		return 0;
275	}
276
277	struct VFileZip* vfz = malloc(sizeof(struct VFileZip));
278	vfz->zf = zf;
279	vfz->buffer = 0;
280	vfz->offset = 0;
281	vfz->bufferSize = 0;
282	vfz->readSize = 0;
283	vfz->fileSize = s.size;
284
285	vfz->d.close = _vfzClose;
286	vfz->d.seek = _vfzSeek;
287	vfz->d.read = _vfzRead;
288	vfz->d.readline = _vfzReadline;
289	vfz->d.write = _vfzWrite;
290	vfz->d.map = _vfzMap;
291	vfz->d.unmap = _vfzUnmap;
292	vfz->d.truncate = _vfzTruncate;
293
294	return &vfz->d;
295}
296
297const char* _vdezName(struct VDirEntry* vde) {
298	struct VDirEntryZip* vdez = (struct VDirEntryZip*) vde;
299	struct zip_stat s;
300	if (zip_stat_index(vdez->z, vdez->index, 0, &s) < 0) {
301		return 0;
302	}
303	return s.name;
304}
305
306#endif