all repos — mgba @ 513b9fb7518e9c06da0ec9ee609bdd72a1344c77

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