all repos — mgba @ 542662ca682ce0df57849bde1636d5d45d764450

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 ENABLE_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 _vfzReadline(struct VFile* vf, char* buffer, size_t size);
 42static ssize_t _vfzWrite(struct VFile* vf, const void* buffer, size_t size);
 43static void* _vfzMap(struct VFile* vf, size_t size, int flags);
 44static void _vfzUnmap(struct VFile* vf, void* memory, size_t size);
 45static void _vfzTruncate(struct VFile* vf, size_t size);
 46static ssize_t _vfzSize(struct VFile* vf);
 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 _vfzReadline(struct VFile* vf, char* buffer, size_t size) {
192	size_t bytesRead = 0;
193	while (bytesRead < size - 1) {
194		size_t newRead = vf->read(vf, &buffer[bytesRead], 1);
195		bytesRead += newRead;
196		if (!newRead || buffer[bytesRead] == '\n') {
197			break;
198		}
199	}
200	return buffer[bytesRead] = '\0';
201}
202
203ssize_t _vfzWrite(struct VFile* vf, const void* buffer, size_t size) {
204	// TODO
205	UNUSED(vf);
206	UNUSED(buffer);
207	UNUSED(size);
208	return -1;
209}
210
211void* _vfzMap(struct VFile* vf, size_t size, int flags) {
212	struct VFileZip* vfz = (struct VFileZip*) vf;
213
214	UNUSED(flags);
215	if (size > vfz->readSize) {
216		vf->read(vf, 0, size - vfz->readSize);
217	}
218	return vfz->buffer;
219}
220
221void _vfzUnmap(struct VFile* vf, void* memory, size_t size) {
222	UNUSED(vf);
223	UNUSED(memory);
224	UNUSED(size);
225}
226
227void _vfzTruncate(struct VFile* vf, size_t size) {
228	// TODO
229	UNUSED(vf);
230	UNUSED(size);
231}
232
233ssize_t _vfzSize(struct VFile* vf) {
234	struct VFileZip* vfz = (struct VFileZip*) vf;
235	return vfz->fileSize;
236}
237
238bool _vdzClose(struct VDir* vd) {
239	struct VDirZip* vdz = (struct VDirZip*) vd;
240	if (zip_close(vdz->z) < 0) {
241		return false;
242	}
243	free(vdz);
244	return true;
245}
246
247void _vdzRewind(struct VDir* vd) {
248	struct VDirZip* vdz = (struct VDirZip*) vd;
249	vdz->dirent.index = -1;
250}
251
252struct VDirEntry* _vdzListNext(struct VDir* vd) {
253	struct VDirZip* vdz = (struct VDirZip*) vd;
254	zip_int64_t maxIndex = zip_get_num_entries(vdz->z, 0);
255	if (maxIndex <= vdz->dirent.index + 1) {
256		return 0;
257	}
258	++vdz->dirent.index;
259	return &vdz->dirent.d;
260}
261
262struct VFile* _vdzOpenFile(struct VDir* vd, const char* path, int mode) {
263	UNUSED(mode);
264	// TODO: support truncating, appending and creating, and write
265	struct VDirZip* vdz = (struct VDirZip*) vd;
266
267	if ((mode & O_RDWR) == O_RDWR) {
268		// libzip doesn't allow for random access, so read/write is impossible without
269		// reading the entire file first. This approach will be supported eventually.
270		return 0;
271	}
272
273	if (mode & O_WRONLY) {
274		// Write support is not yet implemented.
275		return 0;
276	}
277
278	struct zip_stat s;
279	if (zip_stat(vdz->z, path, 0, &s) < 0) {
280		return 0;
281	}
282
283	struct zip_file* zf = zip_fopen(vdz->z, path, 0);
284	if (!zf) {
285		return 0;
286	}
287
288	struct VFileZip* vfz = malloc(sizeof(struct VFileZip));
289	vfz->zf = zf;
290	vfz->buffer = 0;
291	vfz->offset = 0;
292	vfz->bufferSize = 0;
293	vfz->readSize = 0;
294	vfz->fileSize = s.size;
295
296	vfz->d.close = _vfzClose;
297	vfz->d.seek = _vfzSeek;
298	vfz->d.read = _vfzRead;
299	vfz->d.readline = _vfzReadline;
300	vfz->d.write = _vfzWrite;
301	vfz->d.map = _vfzMap;
302	vfz->d.unmap = _vfzUnmap;
303	vfz->d.truncate = _vfzTruncate;
304	vfz->d.size = _vfzSize;
305
306	return &vfz->d;
307}
308
309const char* _vdezName(struct VDirEntry* vde) {
310	struct VDirEntryZip* vdez = (struct VDirEntryZip*) vde;
311	struct zip_stat s;
312	if (zip_stat_index(vdez->z, vdez->index, 0, &s) < 0) {
313		return 0;
314	}
315	return s.name;
316}
317
318#endif