all repos — mgba @ c50bd95258ef067a07bc68ddd27557e61902eafe

mGBA Game Boy Advance Emulator

src/util/vfs-zip.c (view raw)

  1#include "util/vfs.h"
  2
  3#ifdef ENABLE_LIBZIP
  4#include <zip.h>
  5
  6
  7enum {
  8	BLOCK_SIZE = 1024
  9};
 10
 11struct VDirEntryZip {
 12	struct VDirEntry d;
 13	struct zip* z;
 14	zip_int64_t index;
 15};
 16
 17struct VDirZip {
 18	struct VDir d;
 19	struct zip* z;
 20	struct VDirEntryZip dirent;
 21};
 22
 23struct VFileZip {
 24	struct VFile d;
 25	struct zip_file* zf;
 26	void* buffer;
 27	size_t offset;
 28	size_t bufferSize;
 29	size_t readSize;
 30	size_t fileSize;
 31};
 32
 33static bool _vfzClose(struct VFile* vf);
 34static off_t _vfzSeek(struct VFile* vf, off_t offset, int whence);
 35static ssize_t _vfzRead(struct VFile* vf, void* buffer, size_t size);
 36static ssize_t _vfzReadline(struct VFile* vf, char* buffer, size_t size);
 37static ssize_t _vfzWrite(struct VFile* vf, void* buffer, size_t size);
 38static void* _vfzMap(struct VFile* vf, size_t size, int flags);
 39static void _vfzUnmap(struct VFile* vf, void* memory, size_t size);
 40static void _vfzTruncate(struct VFile* vf, size_t size);
 41
 42static bool _vdzClose(struct VDir* vd);
 43static void _vdzRewind(struct VDir* vd);
 44static struct VDirEntry* _vdzListNext(struct VDir* vd);
 45static struct VFile* _vdzOpenFile(struct VDir* vd, const char* path, int mode);
 46
 47static const char* _vdezName(struct VDirEntry* vde);
 48
 49struct VDir* VDirOpenZip(const char* path, int flags) {
 50	int zflags = 0;
 51	if (flags & O_CREAT) {
 52		zflags |= ZIP_CREATE;
 53	}
 54	if (flags & O_EXCL) {
 55		zflags |= ZIP_EXCL;
 56	}
 57
 58	struct zip* z = zip_open(path, zflags, 0);
 59	if (!z) {
 60		return 0;
 61	}
 62	struct VDirZip* vd = malloc(sizeof(struct VDirZip));
 63
 64	vd->d.close = _vdzClose;
 65	vd->d.rewind = _vdzRewind;
 66	vd->d.listNext = _vdzListNext;
 67	vd->d.openFile = _vdzOpenFile;
 68	vd->z = z;
 69
 70	vd->dirent.d.name = _vdezName;
 71	vd->dirent.index = -1;
 72	vd->dirent.z = z;
 73
 74	return &vd->d;
 75}
 76
 77bool _vfzClose(struct VFile* vf) {
 78	struct VFileZip* vfz = (struct VFileZip*) vf;
 79	if (zip_fclose(vfz->zf) < 0) {
 80		return false;
 81	}
 82	free(vfz->buffer);
 83	free(vfz);
 84	return true;
 85}
 86
 87off_t _vfzSeek(struct VFile* vf, off_t offset, int whence) {
 88	struct VFileZip* vfz = (struct VFileZip*) vf;
 89
 90	size_t position;
 91	switch (whence) {
 92	case SEEK_SET:
 93		position = offset;
 94		break;
 95	case SEEK_CUR:
 96		if (offset < 0 && ((vfz->offset < (size_t) -offset) || (offset == INT_MIN))) {
 97			return -1;
 98		}
 99		position = vfz->offset + offset;
100		break;
101	case SEEK_END:
102		if (offset < 0 && ((vfz->fileSize < (size_t) -offset) || (offset == INT_MIN))) {
103			return -1;
104		}
105		position = vfz->fileSize + offset;
106		break;
107	}
108
109	if (position <= vfz->offset) {
110		vfz->offset = position;
111		return position;
112	}
113
114	if (position <= vfz->fileSize) {
115		ssize_t read = vf->read(vf, 0, position - vfz->offset);
116		if (read < 0) {
117			return -1;
118		}
119		return vfz->offset;
120	}
121
122	return -1;
123}
124
125ssize_t _vfzRead(struct VFile* vf, void* buffer, size_t size) {
126	struct VFileZip* vfz = (struct VFileZip*) vf;
127
128	size_t bytesRead = 0;
129	if (!vfz->buffer) {
130		vfz->bufferSize = BLOCK_SIZE;
131		vfz->buffer = malloc(BLOCK_SIZE);
132	}
133
134	while (bytesRead < size) {
135		if (vfz->offset < vfz->readSize) {
136			size_t diff = vfz->readSize - vfz->offset;
137			void* start = &((uint8_t*) vfz->buffer)[vfz->offset];
138			if (diff > size - bytesRead) {
139				diff = size - bytesRead;
140			}
141			if (buffer) {
142				void* bufferOffset = &((uint8_t*) buffer)[bytesRead];
143				memcpy(bufferOffset, start, diff);
144			}
145			vfz->offset += diff;
146			bytesRead += diff;
147			if (diff == size) {
148				break;
149			}
150		}
151		// offset == readSize
152		if (vfz->readSize == vfz->bufferSize) {
153			vfz->bufferSize *= 2;
154			if (vfz->bufferSize > vfz->fileSize) {
155				vfz->bufferSize = vfz->fileSize;
156			}
157			vfz->buffer = realloc(vfz->buffer, vfz->bufferSize);
158		}
159		if (vfz->readSize < vfz->bufferSize) {
160			void* start = &((uint8_t*) vfz->buffer)[vfz->readSize];
161			size_t toRead = vfz->bufferSize - vfz->readSize;
162			if (toRead > BLOCK_SIZE) {
163				toRead = BLOCK_SIZE;
164			}
165			ssize_t zipRead = zip_fread(vfz->zf, start, toRead);
166			if (zipRead < 0) {
167				if (bytesRead == 0) {
168					return -1;
169				}
170				break;
171			}
172			if (zipRead == 0) {
173				break;
174			}
175			vfz->readSize += zipRead;
176		} else {
177			break;
178		}
179	}
180	return bytesRead;
181}
182
183ssize_t _vfzReadline(struct VFile* vf, char* buffer, size_t size) {
184	size_t bytesRead = 0;
185	while (bytesRead < size - 1) {
186		size_t newRead = vf->read(vf, &buffer[bytesRead], 1);
187		bytesRead += newRead;
188		if (!newRead || buffer[bytesRead] == '\n') {
189			break;
190		}
191	}
192	return buffer[bytesRead] = '\0';
193}
194
195ssize_t _vfzWrite(struct VFile* vf, 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
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 = _vfzReadline;
287	vfz->d.write = _vfzWrite;
288	vfz->d.map = _vfzMap;
289	vfz->d.unmap = _vfzUnmap;
290	vfz->d.truncate = _vfzTruncate;
291
292	return &vfz->d;
293}
294
295const char* _vdezName(struct VDirEntry* vde) {
296	struct VDirEntryZip* vdez = (struct VDirEntryZip*) vde;
297	struct zip_stat s;
298	if (zip_stat_index(vdez->z, vdez->index, 0, &s) < 0) {
299		return 0;
300	}
301	return s.name;
302}
303
304#endif