all repos — mgba @ 74fcdf54e048f118df070bc7fbfa3e70e5c95495

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);
 83	return true;
 84}
 85
 86off_t _vfzSeek(struct VFile* vf, off_t offset, int whence) {
 87	struct VFileZip* vfz = (struct VFileZip*) vf;
 88
 89	size_t position;
 90	switch (whence) {
 91	case SEEK_SET:
 92		position = offset;
 93		break;
 94	case SEEK_CUR:
 95		if (offset < 0 && ((vfz->offset < (size_t) -offset) || (offset == INT_MIN))) {
 96			return -1;
 97		}
 98		position = vfz->offset + offset;
 99		break;
100	case SEEK_END:
101		if (offset < 0 && ((vfz->fileSize < (size_t) -offset) || (offset == INT_MIN))) {
102			return -1;
103		}
104		position = vfz->fileSize + offset;
105		break;
106	}
107
108	if (position <= vfz->offset) {
109		vfz->offset = position;
110		return position;
111	}
112
113	if (position <= vfz->fileSize) {
114		ssize_t read = vf->read(vf, 0, position - vfz->offset);
115		if (read < 0) {
116			return -1;
117		}
118		return vfz->offset;
119	}
120
121	return -1;
122}
123
124ssize_t _vfzRead(struct VFile* vf, void* buffer, size_t size) {
125	struct VFileZip* vfz = (struct VFileZip*) vf;
126
127	size_t bytesRead = 0;
128	if (!vfz->buffer) {
129		vfz->bufferSize = BLOCK_SIZE;
130		vfz->buffer = malloc(BLOCK_SIZE);
131	}
132
133	while (bytesRead < size) {
134		if (vfz->offset < vfz->readSize) {
135			size_t diff = vfz->readSize - vfz->offset;
136			void* start = &((uint8_t*) vfz->buffer)[vfz->offset];
137			if (diff > size - bytesRead) {
138				diff = size - bytesRead;
139			}
140			if (buffer) {
141				void* bufferOffset = &((uint8_t*) buffer)[bytesRead];
142				memcpy(bufferOffset, start, diff);
143			}
144			vfz->offset += diff;
145			bytesRead += diff;
146			if (diff == size) {
147				break;
148			}
149		}
150		// offset == readSize
151		if (vfz->readSize == vfz->bufferSize) {
152			vfz->bufferSize *= 2;
153			if (vfz->bufferSize > vfz->fileSize) {
154				vfz->bufferSize = vfz->fileSize;
155			}
156			vfz->buffer = realloc(vfz->buffer, vfz->bufferSize);
157		}
158		if (vfz->readSize < vfz->bufferSize) {
159			void* start = &((uint8_t*) vfz->buffer)[vfz->readSize];
160			size_t toRead = vfz->bufferSize - vfz->readSize;
161			if (toRead > BLOCK_SIZE) {
162				toRead = BLOCK_SIZE;
163			}
164			ssize_t zipRead = zip_fread(vfz->zf, start, toRead);
165			if (zipRead < 0) {
166				if (bytesRead == 0) {
167					return -1;
168				}
169				break;
170			}
171			if (zipRead == 0) {
172				break;
173			}
174			vfz->readSize += zipRead;
175		} else {
176			break;
177		}
178	}
179	return bytesRead;
180}
181
182ssize_t _vfzReadline(struct VFile* vf, char* buffer, size_t size) {
183	size_t bytesRead = 0;
184	while (bytesRead < size - 1) {
185		size_t newRead = vf->read(vf, &buffer[bytesRead], 1);
186		bytesRead += newRead;
187		if (!newRead || buffer[bytesRead] == '\n') {
188			break;
189		}
190	}
191	return buffer[bytesRead] = '\0';
192}
193
194ssize_t _vfzWrite(struct VFile* vf, void* buffer, size_t size) {
195	// TODO
196	UNUSED(vf);
197	UNUSED(buffer);
198	UNUSED(size);
199	return -1;
200}
201
202void* _vfzMap(struct VFile* vf, size_t size, int flags) {
203	struct VFileZip* vfz = (struct VFileZip*) vf;
204
205	UNUSED(flags);
206	if (size > vfz->readSize) {
207		vf->read(vf, 0, size - vfz->readSize);
208	}
209	return vfz->buffer;
210}
211
212void _vfzUnmap(struct VFile* vf, void* memory, size_t size) {
213	UNUSED(vf);
214	UNUSED(memory);
215	UNUSED(size);
216}
217
218void _vfzTruncate(struct VFile* vf, size_t size) {
219	// TODO
220	UNUSED(vf);
221	UNUSED(size);
222}
223
224bool _vdzClose(struct VDir* vd) {
225	struct VDirZip* vdz = (struct VDirZip*) vd;
226	if (zip_close(vdz->z) < 0) {
227		return false;
228	}
229	free(vdz);
230	return true;
231}
232
233void _vdzRewind(struct VDir* vd) {
234	struct VDirZip* vdz = (struct VDirZip*) vd;
235	vdz->dirent.index = -1;
236}
237
238struct VDirEntry* _vdzListNext(struct VDir* vd) {
239	struct VDirZip* vdz = (struct VDirZip*) vd;
240	zip_int64_t maxIndex = zip_get_num_entries(vdz->z, 0);
241	if (maxIndex <= vdz->dirent.index + 1) {
242		return 0;
243	}
244	++vdz->dirent.index;
245	return &vdz->dirent.d;
246}
247
248struct VFile* _vdzOpenFile(struct VDir* vd, const char* path, int mode) {
249	UNUSED(mode);
250	// TODO: support truncating, appending and creating
251
252	struct VDirZip* vdz = (struct VDirZip*) vd;
253	struct zip_stat s;
254	if (zip_stat(vdz->z, path, 0, &s) < 0) {
255		return 0;
256	}
257
258	struct zip_file* zf = zip_fopen(vdz->z, path, 0);
259	if (!zf) {
260		return 0;
261	}
262
263	struct VFileZip* vfz = malloc(sizeof(struct VFileZip));
264	vfz->zf = zf;
265	vfz->buffer = 0;
266	vfz->offset = 0;
267	vfz->bufferSize = 0;
268	vfz->readSize = 0;
269	vfz->fileSize = s.size;
270
271	vfz->d.close = _vfzClose;
272	vfz->d.seek = _vfzSeek;
273	vfz->d.read = _vfzRead;
274	vfz->d.readline = _vfzReadline;
275	vfz->d.write = _vfzWrite;
276	vfz->d.map = _vfzMap;
277	vfz->d.unmap = _vfzUnmap;
278	vfz->d.truncate = _vfzTruncate;
279
280	return &vfz->d;
281}
282
283const char* _vdezName(struct VDirEntry* vde) {
284	struct VDirEntryZip* vdez = (struct VDirEntryZip*) vde;
285	struct zip_stat s;
286	if (zip_stat_index(vdez->z, vdez->index, 0, &s) < 0) {
287		return 0;
288	}
289	return s.name;
290}
291
292#endif