all repos — mgba @ 2e77c45e06d33c52e4b55b5f1bc10a3fd4b9912c

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