all repos — mgba @ 6f8b4114fcade19c74788827788332885c7f72eb

mGBA Game Boy Advance Emulator

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

  1/* Copyright (c) 2013-2014 Jeffrey Pfau
  2 *
  3 * This Source Code Form is subject to the terms of the Mozilla Public
  4 * License, v. 2.0. If a copy of the MPL was not distributed with this
  5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  6#include "util/vfs.h"
  7
  8#ifdef USE_LIBZIP
  9#include <zip.h>
 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
 33enum {
 34	BLOCK_SIZE = 1024
 35};
 36#else
 37#ifdef USE_MINIZIP
 38#include <minizip/unzip.h>
 39#else
 40#include "third-party/zlib/contrib/minizip/unzip.h"
 41#endif
 42#include "util/memory.h"
 43
 44struct VDirEntryZip {
 45	struct VDirEntry d;
 46	char name[PATH_MAX];
 47	size_t fileSize;
 48	unzFile z;
 49};
 50
 51struct VDirZip {
 52	struct VDir d;
 53	unzFile z;
 54	struct VDirEntryZip dirent;
 55	bool hasNextFile;
 56};
 57
 58struct VFileZip {
 59	struct VFile d;
 60	unzFile z;
 61	void* buffer;
 62	size_t fileSize;
 63};
 64#endif
 65
 66static bool _vfzClose(struct VFile* vf);
 67static off_t _vfzSeek(struct VFile* vf, off_t offset, int whence);
 68static ssize_t _vfzRead(struct VFile* vf, void* buffer, size_t size);
 69static ssize_t _vfzWrite(struct VFile* vf, const void* buffer, size_t size);
 70static void* _vfzMap(struct VFile* vf, size_t size, int flags);
 71static void _vfzUnmap(struct VFile* vf, void* memory, size_t size);
 72static void _vfzTruncate(struct VFile* vf, size_t size);
 73static ssize_t _vfzSize(struct VFile* vf);
 74static bool _vfzSync(struct VFile* vf, const void* buffer, size_t size);
 75
 76static bool _vdzClose(struct VDir* vd);
 77static void _vdzRewind(struct VDir* vd);
 78static struct VDirEntry* _vdzListNext(struct VDir* vd);
 79static struct VFile* _vdzOpenFile(struct VDir* vd, const char* path, int mode);
 80static struct VDir* _vdzOpenDir(struct VDir* vd, const char* path);
 81static bool _vdzDeleteFile(struct VDir* vd, const char* path);
 82
 83static const char* _vdezName(struct VDirEntry* vde);
 84static enum VFSType _vdezType(struct VDirEntry* vde);
 85
 86#ifndef USE_LIBZIP
 87static voidpf _vfmzOpen(voidpf opaque, const char* filename, int mode) {
 88	UNUSED(opaque);
 89	int flags = 0;
 90	switch (mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) {
 91	case ZLIB_FILEFUNC_MODE_READ:
 92		flags = O_RDONLY;
 93		break;
 94	case ZLIB_FILEFUNC_MODE_WRITE:
 95		flags = O_WRONLY;
 96		break;
 97	case ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE:
 98		flags = O_RDWR;
 99		break;
100	}
101	if (mode & ZLIB_FILEFUNC_MODE_CREATE) {
102		flags |= O_CREAT;
103	}
104	return VFileOpen(filename, flags);
105}
106
107static uLong _vfmzRead(voidpf opaque, voidpf stream, void* buf, uLong size) {
108	UNUSED(opaque);
109	struct VFile* vf = stream;
110	ssize_t r = vf->read(vf, buf, size);
111	if (r < 0) {
112		return 0;
113	}
114	return r;
115}
116
117int _vfmzClose(voidpf opaque, voidpf stream) {
118	UNUSED(opaque);
119	struct VFile* vf = stream;
120	return vf->close(vf);
121}
122
123int _vfmzError(voidpf opaque, voidpf stream) {
124	UNUSED(opaque);
125	struct VFile* vf = stream;
126	return vf->seek(vf, 0, SEEK_CUR) < 0;
127}
128
129long _vfmzTell(voidpf opaque, voidpf stream) {
130	UNUSED(opaque);
131	struct VFile* vf = stream;
132	return vf->seek(vf, 0, SEEK_CUR);
133}
134
135long _vfmzSeek(voidpf opaque, voidpf stream, uLong offset, int origin) {
136	UNUSED(opaque);
137	struct VFile* vf = stream;
138	return vf->seek(vf, offset, origin) < 0;
139}
140#endif
141
142struct VDir* VDirOpenZip(const char* path, int flags) {
143#ifndef USE_LIBZIP
144	UNUSED(flags);
145	zlib_filefunc_def ops = {
146		.zopen_file = _vfmzOpen,
147		.zread_file = _vfmzRead,
148		.zwrite_file = 0,
149		.ztell_file = _vfmzTell,
150		.zseek_file = _vfmzSeek,
151		.zclose_file = _vfmzClose,
152		.zerror_file = _vfmzError,
153		.opaque = 0
154	};
155	unzFile z = unzOpen2(path, &ops);
156	if (!z) {
157		return 0;
158	}
159#else
160	int zflags = 0;
161	if (flags & O_CREAT) {
162		zflags |= ZIP_CREATE;
163	}
164	if (flags & O_EXCL) {
165		zflags |= ZIP_EXCL;
166	}
167
168	struct zip* z = zip_open(path, zflags, 0);
169	if (!z) {
170		return 0;
171	}
172#endif
173	struct VDirZip* vd = malloc(sizeof(struct VDirZip));
174
175	vd->d.close = _vdzClose;
176	vd->d.rewind = _vdzRewind;
177	vd->d.listNext = _vdzListNext;
178	vd->d.openFile = _vdzOpenFile;
179	vd->d.openDir = _vdzOpenDir;
180	vd->d.deleteFile = _vdzDeleteFile;
181	vd->z = z;
182
183#ifndef USE_LIBZIP
184	vd->hasNextFile = true;
185#endif
186
187	vd->dirent.d.name = _vdezName;
188	vd->dirent.d.type = _vdezType;
189#ifdef USE_LIBZIP
190	vd->dirent.index = -1;
191#endif
192	vd->dirent.z = z;
193
194	return &vd->d;
195}
196
197#ifdef USE_LIBZIP
198bool _vfzClose(struct VFile* vf) {
199	struct VFileZip* vfz = (struct VFileZip*) vf;
200	if (zip_fclose(vfz->zf) < 0) {
201		return false;
202	}
203	free(vfz->buffer);
204	free(vfz);
205	return true;
206}
207
208off_t _vfzSeek(struct VFile* vf, off_t offset, int whence) {
209	struct VFileZip* vfz = (struct VFileZip*) vf;
210
211	size_t position;
212	switch (whence) {
213	case SEEK_SET:
214		position = offset;
215		break;
216	case SEEK_CUR:
217		if (offset < 0 && ((vfz->offset < (size_t) -offset) || (offset == INT_MIN))) {
218			return -1;
219		}
220		position = vfz->offset + offset;
221		break;
222	case SEEK_END:
223		if (offset < 0 && ((vfz->fileSize < (size_t) -offset) || (offset == INT_MIN))) {
224			return -1;
225		}
226		position = vfz->fileSize + offset;
227		break;
228	default:
229		return -1;
230	}
231
232	if (position <= vfz->offset) {
233		vfz->offset = position;
234		return position;
235	}
236
237	if (position <= vfz->fileSize) {
238		ssize_t read = vf->read(vf, 0, position - vfz->offset);
239		if (read < 0) {
240			return -1;
241		}
242		return vfz->offset;
243	}
244
245	return -1;
246}
247
248ssize_t _vfzRead(struct VFile* vf, void* buffer, size_t size) {
249	struct VFileZip* vfz = (struct VFileZip*) vf;
250
251	size_t bytesRead = 0;
252	if (!vfz->buffer) {
253		vfz->bufferSize = BLOCK_SIZE;
254		vfz->buffer = malloc(BLOCK_SIZE);
255	}
256
257	while (bytesRead < size) {
258		if (vfz->offset < vfz->readSize) {
259			size_t diff = vfz->readSize - vfz->offset;
260			void* start = &((uint8_t*) vfz->buffer)[vfz->offset];
261			if (diff > size - bytesRead) {
262				diff = size - bytesRead;
263			}
264			if (buffer) {
265				void* bufferOffset = &((uint8_t*) buffer)[bytesRead];
266				memcpy(bufferOffset, start, diff);
267			}
268			vfz->offset += diff;
269			bytesRead += diff;
270			if (diff == size) {
271				break;
272			}
273		}
274		// offset == readSize
275		if (vfz->readSize == vfz->bufferSize) {
276			vfz->bufferSize *= 2;
277			if (vfz->bufferSize > vfz->fileSize) {
278				vfz->bufferSize = vfz->fileSize;
279			}
280			vfz->buffer = realloc(vfz->buffer, vfz->bufferSize);
281		}
282		if (vfz->readSize < vfz->bufferSize) {
283			void* start = &((uint8_t*) vfz->buffer)[vfz->readSize];
284			size_t toRead = vfz->bufferSize - vfz->readSize;
285			if (toRead > BLOCK_SIZE) {
286				toRead = BLOCK_SIZE;
287			}
288			ssize_t zipRead = zip_fread(vfz->zf, start, toRead);
289			if (zipRead < 0) {
290				if (bytesRead == 0) {
291					return -1;
292				}
293				break;
294			}
295			if (zipRead == 0) {
296				break;
297			}
298			vfz->readSize += zipRead;
299		} else {
300			break;
301		}
302	}
303	return bytesRead;
304}
305
306ssize_t _vfzWrite(struct VFile* vf, const void* buffer, size_t size) {
307	// TODO
308	UNUSED(vf);
309	UNUSED(buffer);
310	UNUSED(size);
311	return -1;
312}
313
314void* _vfzMap(struct VFile* vf, size_t size, int flags) {
315	struct VFileZip* vfz = (struct VFileZip*) vf;
316
317	UNUSED(flags);
318	if (size > vfz->readSize) {
319		vf->read(vf, 0, size - vfz->readSize);
320	}
321	return vfz->buffer;
322}
323
324void _vfzUnmap(struct VFile* vf, void* memory, size_t size) {
325	UNUSED(vf);
326	UNUSED(memory);
327	UNUSED(size);
328}
329
330void _vfzTruncate(struct VFile* vf, size_t size) {
331	// TODO
332	UNUSED(vf);
333	UNUSED(size);
334}
335
336ssize_t _vfzSize(struct VFile* vf) {
337	struct VFileZip* vfz = (struct VFileZip*) vf;
338	return vfz->fileSize;
339}
340
341bool _vdzClose(struct VDir* vd) {
342	struct VDirZip* vdz = (struct VDirZip*) vd;
343	if (zip_close(vdz->z) < 0) {
344		return false;
345	}
346	free(vdz);
347	return true;
348}
349
350void _vdzRewind(struct VDir* vd) {
351	struct VDirZip* vdz = (struct VDirZip*) vd;
352	vdz->dirent.index = -1;
353}
354
355struct VDirEntry* _vdzListNext(struct VDir* vd) {
356	struct VDirZip* vdz = (struct VDirZip*) vd;
357	zip_int64_t maxIndex = zip_get_num_entries(vdz->z, 0);
358	if (maxIndex <= vdz->dirent.index + 1) {
359		return 0;
360	}
361	++vdz->dirent.index;
362	return &vdz->dirent.d;
363}
364
365struct VFile* _vdzOpenFile(struct VDir* vd, const char* path, int mode) {
366	UNUSED(mode);
367	// TODO: support truncating, appending and creating, and write
368	struct VDirZip* vdz = (struct VDirZip*) vd;
369
370	if ((mode & O_RDWR) == O_RDWR) {
371		// libzip doesn't allow for random access, so read/write is impossible without
372		// reading the entire file first. This approach will be supported eventually.
373		return 0;
374	}
375
376	if (mode & O_WRONLY) {
377		// Write support is not yet implemented.
378		return 0;
379	}
380
381	struct zip_stat s;
382	if (zip_stat(vdz->z, path, 0, &s) < 0) {
383		return 0;
384	}
385
386	struct zip_file* zf = zip_fopen(vdz->z, path, 0);
387	if (!zf) {
388		return 0;
389	}
390
391	struct VFileZip* vfz = malloc(sizeof(struct VFileZip));
392	vfz->zf = zf;
393	vfz->buffer = 0;
394	vfz->offset = 0;
395	vfz->bufferSize = 0;
396	vfz->readSize = 0;
397	vfz->fileSize = s.size;
398
399	vfz->d.close = _vfzClose;
400	vfz->d.seek = _vfzSeek;
401	vfz->d.read = _vfzRead;
402	vfz->d.readline = VFileReadline;
403	vfz->d.write = _vfzWrite;
404	vfz->d.map = _vfzMap;
405	vfz->d.unmap = _vfzUnmap;
406	vfz->d.truncate = _vfzTruncate;
407	vfz->d.size = _vfzSize;
408	vfz->d.sync = _vfzSync;
409
410	return &vfz->d;
411}
412
413struct VDir* _vdzOpenDir(struct VDir* vd, const char* path) {
414	UNUSED(vd);
415	UNUSED(path);
416	return 0;
417}
418
419bool _vdzDeleteFile(struct VDir* vd, const char* path) {
420	UNUSED(vd);
421	UNUSED(path);
422	// TODO
423	return false;
424}
425
426bool _vfzSync(struct VFile* vf, const void* memory, size_t size) {
427	UNUSED(vf);
428	UNUSED(memory);
429	UNUSED(size);
430	return false;
431}
432
433const char* _vdezName(struct VDirEntry* vde) {
434	struct VDirEntryZip* vdez = (struct VDirEntryZip*) vde;
435	struct zip_stat s;
436	if (zip_stat_index(vdez->z, vdez->index, 0, &s) < 0) {
437		return 0;
438	}
439	return s.name;
440}
441
442static enum VFSType _vdezType(struct VDirEntry* vde) {
443	struct VDirEntryZip* vdez = (struct VDirEntryZip*) vde;
444	UNUSED(vdez);
445	return VFS_UNKNOWN;
446}
447#else
448bool _vfzClose(struct VFile* vf) {
449	struct VFileZip* vfz = (struct VFileZip*) vf;
450	unzCloseCurrentFile(vfz->z);
451	free(vfz->buffer);
452	free(vfz);
453	return true;
454}
455
456off_t _vfzSeek(struct VFile* vf, off_t offset, int whence) {
457	struct VFileZip* vfz = (struct VFileZip*) vf;
458
459	int64_t currentPos = unztell64(vfz->z);
460	int64_t pos;
461	switch (whence) {
462	case SEEK_SET:
463		pos = 0;
464		break;
465	case SEEK_CUR:
466		pos = unztell64(vfz->z);
467		break;
468	case SEEK_END:
469		pos = vfz->fileSize;
470		break;
471	default:
472		return -1;
473	}
474
475	if (pos < 0 || pos + offset < 0) {
476		return -1;
477	}
478	pos += offset;
479	if (currentPos > pos) {
480		unzCloseCurrentFile(vfz->z);
481		unzOpenCurrentFile(vfz->z);
482		currentPos = 0;
483	}
484	while (currentPos < pos) {
485		char tempBuf[1024];
486		ssize_t toRead = sizeof(tempBuf);
487		if (toRead > pos - currentPos) {
488			toRead = pos - currentPos;
489		}
490		ssize_t read = vf->read(vf, tempBuf, toRead);
491		if (read < toRead) {
492			return -1;
493		}
494		currentPos += read;
495	}
496
497	return unztell64(vfz->z);
498}
499
500ssize_t _vfzRead(struct VFile* vf, void* buffer, size_t size) {
501	struct VFileZip* vfz = (struct VFileZip*) vf;
502	return unzReadCurrentFile(vfz->z, buffer, size);
503}
504
505ssize_t _vfzWrite(struct VFile* vf, const void* buffer, size_t size) {
506	// TODO
507	UNUSED(vf);
508	UNUSED(buffer);
509	UNUSED(size);
510	return -1;
511}
512
513void* _vfzMap(struct VFile* vf, size_t size, int flags) {
514	struct VFileZip* vfz = (struct VFileZip*) vf;
515
516	// TODO
517	UNUSED(flags);
518
519	off_t pos = vf->seek(vf, 0, SEEK_CUR);
520	if (pos < 0) {
521		return 0;
522	}
523
524	vfz->buffer = anonymousMemoryMap(size);
525	if (!vfz->buffer) {
526		return 0;
527	}
528
529	unzCloseCurrentFile(vfz->z);
530	unzOpenCurrentFile(vfz->z);
531	vf->read(vf, vfz->buffer, size);
532	unzCloseCurrentFile(vfz->z);
533	unzOpenCurrentFile(vfz->z);
534	vf->seek(vf, pos, SEEK_SET);
535
536	return vfz->buffer;
537}
538
539void _vfzUnmap(struct VFile* vf, void* memory, size_t size) {
540	struct VFileZip* vfz = (struct VFileZip*) vf;
541
542	if (memory != vfz->buffer) {
543		return;
544	}
545
546	mappedMemoryFree(vfz->buffer, size);
547	vfz->buffer = 0;
548}
549
550void _vfzTruncate(struct VFile* vf, size_t size) {
551	// TODO
552	UNUSED(vf);
553	UNUSED(size);
554}
555
556ssize_t _vfzSize(struct VFile* vf) {
557	struct VFileZip* vfz = (struct VFileZip*) vf;
558	return vfz->fileSize;
559}
560
561bool _vdzClose(struct VDir* vd) {
562	struct VDirZip* vdz = (struct VDirZip*) vd;
563	if (unzClose(vdz->z) < 0) {
564		return false;
565	}
566	free(vdz);
567	return true;
568}
569
570void _vdzRewind(struct VDir* vd) {
571	struct VDirZip* vdz = (struct VDirZip*) vd;
572	vdz->hasNextFile = unzGoToFirstFile(vdz->z) == UNZ_OK;
573}
574
575struct VDirEntry* _vdzListNext(struct VDir* vd) {
576	struct VDirZip* vdz = (struct VDirZip*) vd;
577	if (!vdz->hasNextFile) {
578		return 0;
579	}
580	unz_file_info64 info;
581	int status = unzGetCurrentFileInfo64(vdz->z, &info, vdz->dirent.name, sizeof(vdz->dirent.name), 0, 0, 0, 0);
582	if (status < 0) {
583		return 0;
584	}
585	vdz->dirent.fileSize = info.uncompressed_size;
586	if (unzGoToNextFile(vdz->z) == UNZ_END_OF_LIST_OF_FILE) {
587		vdz->hasNextFile = false;
588	}
589	return &vdz->dirent.d;
590}
591
592struct VFile* _vdzOpenFile(struct VDir* vd, const char* path, int mode) {
593	UNUSED(mode);
594	struct VDirZip* vdz = (struct VDirZip*) vd;
595
596	if ((mode & O_ACCMODE) != O_RDONLY) {
597		// minizip implementation only supports read
598		return 0;
599	}
600
601	if (unzLocateFile(vdz->z, path, 0) != UNZ_OK) {
602		return 0;
603	}
604
605	if (unzOpenCurrentFile(vdz->z) < 0) {
606		return 0;
607	}
608
609	unz_file_info64 info;
610	int status = unzGetCurrentFileInfo64(vdz->z, &info, 0, 0, 0, 0, 0, 0);
611	if (status < 0) {
612		return 0;
613	}
614
615	struct VFileZip* vfz = malloc(sizeof(struct VFileZip));
616	vfz->z = vdz->z;
617	vfz->buffer = 0;
618	vfz->fileSize = info.uncompressed_size;
619
620	vfz->d.close = _vfzClose;
621	vfz->d.seek = _vfzSeek;
622	vfz->d.read = _vfzRead;
623	vfz->d.readline = VFileReadline;
624	vfz->d.write = _vfzWrite;
625	vfz->d.map = _vfzMap;
626	vfz->d.unmap = _vfzUnmap;
627	vfz->d.truncate = _vfzTruncate;
628	vfz->d.size = _vfzSize;
629	vfz->d.sync = _vfzSync;
630
631	return &vfz->d;
632}
633
634struct VDir* _vdzOpenDir(struct VDir* vd, const char* path) {
635	UNUSED(vd);
636	UNUSED(path);
637	return 0;
638}
639
640bool _vdzDeleteFile(struct VDir* vd, const char* path) {
641	UNUSED(vd);
642	UNUSED(path);
643	// TODO
644	return false;
645}
646
647bool _vfzSync(struct VFile* vf, const void* memory, size_t size) {
648	UNUSED(vf);
649	UNUSED(memory);
650	UNUSED(size);
651	return false;
652}
653
654const char* _vdezName(struct VDirEntry* vde) {
655	struct VDirEntryZip* vdez = (struct VDirEntryZip*) vde;
656	return vdez->name;
657}
658
659static enum VFSType _vdezType(struct VDirEntry* vde) {
660	struct VDirEntryZip* vdez = (struct VDirEntryZip*) vde;
661	UNUSED(vdez);
662	return VFS_UNKNOWN;
663}
664#endif