all repos — mgba @ 250d3b940dc06961a320811678dac67e5a77484f

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