all repos — mgba @ 50402c830729f2ba5a6fc3e6facfd8b258f7f97d

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