all repos — mgba @ f6a7fedb2813d070a07cd6da65e8ddd666cd41d1

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