all repos — mgba @ c14da05d8dca225010677643c32fea5c0ac8517a

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