all repos — mgba @ d662ba98de41c22489ca4d42ae3291fe043aa13e

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